0
|
1 /* Copyright 2010 Ivo Smits <Ivo@UCIS.nl>. All rights reserved. |
|
2 Redistribution and use in source and binary forms, with or without modification, are |
|
3 permitted provided that the following conditions are met: |
|
4 |
|
5 1. Redistributions of source code must retain the above copyright notice, this list of |
|
6 conditions and the following disclaimer. |
|
7 |
|
8 2. Redistributions in binary form must reproduce the above copyright notice, this list |
|
9 of conditions and the following disclaimer in the documentation and/or other materials |
|
10 provided with the distribution. |
|
11 |
|
12 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
13 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
14 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR |
|
15 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
16 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
17 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|
18 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
19 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
20 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
21 |
|
22 The views and conclusions contained in the software and documentation are those of the |
|
23 authors and should not be interpreted as representing official policies, either expressed |
|
24 or implied, of Ivo Smits.*/ |
|
25 |
|
26 #include <stdio.h> |
|
27 #include <stdlib.h> |
|
28 #include <string.h> |
|
29 #include <fcntl.h> |
|
30 #ifndef HAVE_NETINET_IN_H |
|
31 #include <netinet/in.h> |
|
32 #endif |
|
33 #include <sys/ioctl.h> |
|
34 #include <linux/if.h> |
|
35 #include <linux/if_tun.h> |
|
36 #include <linux/if_ether.h> |
|
37 #include <poll.h> |
|
38 #include <netdb.h> |
|
39 #include <stdlib.h> |
|
40 |
|
41 #define MAX_PACKET_LEN (ETH_FRAME_LEN+4) //Some space for optional packet information |
|
42 |
|
43 struct qtsession; |
|
44 struct qtproto { |
|
45 int encrypted; |
|
46 int buffersize_raw; |
|
47 int buffersize_enc; |
|
48 int offset_raw; |
|
49 int offset_enc; |
|
50 int (*encode)(struct qtsession* sess, char* raw, char* enc, int len); |
|
51 int (*decode)(struct qtsession* sess, char* enc, char* raw, int len); |
|
52 int (*init)(struct qtsession* sess); |
|
53 int protocol_data_size; |
|
54 }; |
|
55 struct qtsession { |
|
56 struct qtproto protocol; |
|
57 void* protocol_data; |
|
58 int fd_socket; |
|
59 int fd_dev; |
|
60 int remote_float; |
|
61 struct sockaddr_in remote_addr; |
|
62 }; |
|
63 |
|
64 #ifdef COMBINED_BINARY |
|
65 extern char* (*getconf)(const char*); |
|
66 extern int errorexit(const char*); |
|
67 extern int errorexitp(const char*); |
|
68 extern void print_header(); |
|
69 extern void hex2bin(unsigned char*, unsigned char*, int); |
|
70 #else |
|
71 |
|
72 char* (*getconf)(const char*) = getenv; |
|
73 |
|
74 int errorexit(const char* text) { |
|
75 fprintf(stderr, "%s\n", text); |
|
76 return -1; |
|
77 } |
|
78 int errorexitp(const char* text) { |
|
79 perror(text); |
|
80 return -1; |
|
81 } |
|
82 |
|
83 void print_header() { |
|
84 printf("UCIS QuickTun (c) 2010 Ivo Smits <Ivo@UCIS.nl>\n"); |
|
85 printf("More information: http://wiki.qontrol.nl/QuickTun\n"); |
|
86 } |
|
87 |
|
88 int init_udp(struct qtsession* session) { |
|
89 char* envval; |
|
90 printf("Initializing UDP socket...\n"); |
|
91 int sfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
92 if (sfd < 0) return errorexitp("Could not create UDP socket"); |
|
93 struct sockaddr_in udpaddr; |
|
94 struct hostent *he; |
|
95 udpaddr.sin_family = AF_INET; |
|
96 udpaddr.sin_addr.s_addr = INADDR_ANY; |
|
97 udpaddr.sin_port = htons(2998); |
|
98 if (envval = getconf("LOCAL_ADDRESS")) { |
|
99 he = gethostbyname(envval); |
|
100 if (!he) return errorexit("bind address lookup failed"); |
|
101 else if (!he->h_addr_list[0]) return errorexit("no address to bind to"); |
|
102 udpaddr.sin_addr.s_addr = *((unsigned long*)he->h_addr_list[0]); |
|
103 udpaddr.sin_family = he->h_addrtype; |
|
104 } |
|
105 if (envval = getconf("LOCAL_PORT")) { |
|
106 udpaddr.sin_port = htons(atoi(envval)); |
|
107 } |
|
108 if (bind(sfd, (struct sockaddr*)&udpaddr, sizeof(struct sockaddr_in))) return errorexitp("Could not bind socket"); |
|
109 if (!(envval = getconf("REMOTE_ADDRESS"))) { |
|
110 session->remote_float = 1; |
|
111 //return errorexit("Missing REMOTE_ADDRESS"); |
|
112 } else { |
|
113 session->remote_float = 0; |
|
114 he = gethostbyname(envval); |
|
115 if (!he) return errorexit("remote address lookup failed"); |
|
116 else if (!he->h_addr_list[0]) return errorexit("no address to connect to"); |
|
117 udpaddr.sin_family = he->h_addrtype; |
|
118 udpaddr.sin_addr.s_addr = *((unsigned long*)he->h_addr_list[0]); |
|
119 if (envval = getconf("REMOTE_PORT")) { |
|
120 udpaddr.sin_port = htons(atoi(envval)); |
|
121 } |
|
122 if (connect(sfd, (struct sockaddr*)&udpaddr, sizeof(struct sockaddr_in))) return errorexitp("Could not connect socket"); |
|
123 session->remote_addr = udpaddr; |
|
124 } |
|
125 session->fd_socket = sfd; |
|
126 return sfd; |
|
127 } |
|
128 |
|
129 int init_tuntap() { |
|
130 char* envval; |
|
131 printf("Initializing tap device...\n"); |
|
132 int ttfd; //Tap device file descriptor |
|
133 struct ifreq ifr; //required for tun/tap setup |
|
134 memset(&ifr, 0, sizeof(ifr)); |
|
135 if ((ttfd = open("/dev/net/tun", O_RDWR)) < 0) return errorexitp("Could not open tap device file"); |
|
136 if (envval = getconf("INTERFACE")) strcpy(ifr.ifr_name, envval); |
|
137 ifr.ifr_flags = getconf("TUN_MODE") ? IFF_TUN : IFF_TAP; |
|
138 ifr.ifr_flags |= getconf("USE_PI") ? 0 : IFF_NO_PI; |
|
139 if (ioctl(ttfd, TUNSETIFF, (void *)&ifr) < 0) return errorexitp("TUNSETIFF ioctl failed"); |
|
140 return ttfd; |
|
141 } |
|
142 |
|
143 void hex2bin(unsigned char* dest, unsigned char* src, int count) { |
|
144 int i; |
|
145 for (i = 0; i < count; i++) { |
|
146 if (*src >= '0' && *src <= '9') *dest = *src - '0'; |
|
147 else if (*src >= 'a' && * src <='f') *dest = *src - 'a' + 10; |
|
148 else if (*src >= 'A' && * src <='F') *dest = *src - 'A' + 10; |
|
149 src++; *dest = *dest << 4; |
|
150 if (*src >= '0' && *src <= '9') *dest += *src - '0'; |
|
151 else if (*src >= 'a' && *src <= 'f') *dest += *src - 'a' + 10; |
|
152 else if (*src >= 'A' && *src <= 'F') *dest += *src - 'A' + 10; |
|
153 src++; dest++; |
|
154 } |
|
155 } |
|
156 |
|
157 int qtrun(struct qtproto* p) { |
|
158 struct qtsession session; |
|
159 session.protocol = *p; |
|
160 init_udp(&session); |
|
161 session.fd_dev = init_tuntap(); |
|
162 |
|
163 char protocol_data[p->protocol_data_size]; |
|
164 session.protocol_data = &protocol_data; |
|
165 if (p->init) p->init(&session); |
|
166 |
|
167 int sfd = session.fd_socket; |
|
168 int ttfd = session.fd_dev; |
|
169 if (sfd == -1) return -1; |
|
170 if (ttfd == -1) return -1; |
|
171 printf("The tunnel is now operational!\n"); |
|
172 |
|
173 struct pollfd fds[2]; |
|
174 fds[0].fd = ttfd; |
|
175 fds[0].events = POLLIN; |
|
176 fds[1].fd = sfd; |
|
177 fds[1].events = POLLIN; |
|
178 |
|
179 struct sockaddr_in recvaddr; |
|
180 |
|
181 char buffer_raw_a[p->buffersize_raw]; |
|
182 char buffer_enc_a[p->buffersize_enc]; |
|
183 char* buffer_raw = buffer_raw_a; |
|
184 char* buffer_enc = buffer_enc_a; |
|
185 |
|
186 while (1) { |
|
187 int len = poll(fds, 2, -1); |
|
188 if (len < 0) return errorexitp("poll error"); |
|
189 else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) return errorexit("poll error on tap device"); |
|
190 else if (fds[1].revents & (POLLHUP | POLLNVAL)) return errorexit("poll error on udp socket"); |
|
191 if (fds[0].revents & POLLIN) { |
|
192 if (session.remote_float == 0 || session.remote_float == 2) { |
|
193 len = read(ttfd, buffer_raw + p->offset_raw, p->buffersize_raw); |
|
194 len = p->encode(&session, buffer_raw, buffer_enc, len); |
|
195 if (len < 0) return len; |
|
196 if (session.remote_float == 0) { |
|
197 write(sfd, buffer_enc + p->offset_enc, len); |
|
198 } else { |
|
199 sendto(sfd, buffer_enc + p->offset_enc, len, 0, (struct sockaddr*)&session.remote_addr, sizeof(session.remote_addr)); |
|
200 } |
|
201 } |
|
202 } |
|
203 if (fds[1].revents & POLLIN) { |
|
204 socklen_t recvaddr_len = sizeof(recvaddr); |
|
205 if (session.remote_float == 0) { |
|
206 len = read(sfd, buffer_enc + p->offset_enc, p->buffersize_enc); |
|
207 } else { |
|
208 len = recvfrom(sfd, buffer_enc + p->offset_enc, p->buffersize_enc, 0, (struct sockaddr*)&recvaddr, &recvaddr_len); |
|
209 } |
|
210 if (len < 0) { |
|
211 int out; |
|
212 len = 4; |
|
213 getsockopt(sfd, SOL_SOCKET, SO_ERROR, &out, &len); |
|
214 fprintf(stderr, "End of file on udp socket"); |
|
215 } else { |
|
216 len = p->decode(&session, buffer_enc, buffer_raw, len); |
|
217 if (len != 0 && session.remote_float != 0 && (session.remote_addr.sin_addr.s_addr != recvaddr.sin_addr.s_addr || session.remote_addr.sin_port != recvaddr.sin_port)) { |
|
218 fprintf(stderr, "Remote endpoint has changed to %s:%d", inet_ntoa(recvaddr.sin_addr), ntohs(recvaddr.sin_port)); |
|
219 session.remote_addr = recvaddr; |
|
220 session.remote_float = 2; |
|
221 } |
|
222 if (len < 0) return len; |
|
223 write(ttfd, buffer_raw + p->offset_raw, len); |
|
224 } |
|
225 } |
|
226 } |
|
227 return 0; |
|
228 } |
|
229 #endif |