comparison main.c @ 0:17cb7cdbb8be draft default tip

Working prototype
author Ivo Smits <Ivo@UCIS.nl>
date Fri, 07 Feb 2014 23:28:39 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:17cb7cdbb8be
1 /* Copyright 2014 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 <stdlib.h>
27 #include <stdio.h>
28 #include <stdbool.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <poll.h>
33 #include <sys/socket.h>
34 #ifndef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
36 #endif
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include "include.h"
40
41 char* (*getconf)(const char*) = getenv;
42
43 static void hex2bin(unsigned char* dest, const char* src, const int count) {
44 int i;
45 for (i = 0; i < count; i++) {
46 if (*src >= '0' && *src <= '9') *dest = *src - '0';
47 else if (*src >= 'a' && * src <='f') *dest = *src - 'a' + 10;
48 else if (*src >= 'A' && * src <='F') *dest = *src - 'A' + 10;
49 src++; *dest = *dest << 4;
50 if (*src >= '0' && *src <= '9') *dest += *src - '0';
51 else if (*src >= 'a' && *src <= 'f') *dest += *src - 'a' + 10;
52 else if (*src >= 'A' && *src <= 'F') *dest += *src - 'A' + 10;
53 src++; dest++;
54 }
55 }
56
57 static bool crypto_init(connection_context* context, bool* keyupdate) {
58 unsigned char cpublickey[32], csecretkey[32];
59 bool hpublickey = false, hsecretkey = false;
60 char* envval;
61 *keyupdate = false;
62 if ((envval = getconf("PUBLIC_KEY"))) {
63 if (strlen(envval) != 64) return errorexit("PUBLIC_KEY length");
64 hex2bin(cpublickey, envval, 32);
65 hpublickey = true;
66 }
67 if ((envval = getconf("PRIVATE_KEY"))) {
68 if (strlen(envval) != 64) return errorexit("PRIVATE_KEY length");
69 hex2bin(csecretkey, envval, 32);
70 hsecretkey = true;
71 } else if ((envval = getconf("PRIVATE_KEY_FILE"))) {
72 FILE* pkfile = fopen(envval, "rb");
73 if (!pkfile) return errorexitp("Could not open PRIVATE_KEY_FILE");
74 char pktextbuf[64];
75 const size_t pktextsize = fread(pktextbuf, 1, sizeof(pktextbuf), pkfile);
76 if (pktextsize == 32) {
77 memcpy(csecretkey, pktextbuf, 32);
78 } else if (pktextsize == 64) {
79 hex2bin(csecretkey, pktextbuf, 32);
80 } else {
81 return errorexit("PRIVATE_KEY length");
82 }
83 fclose(pkfile);
84 hsecretkey = true;
85 }
86 if (hpublickey || hsecretkey || ((envval = getconf("ENCRYPT")) && atoi(envval))) {
87 if (!hpublickey) fprintf(stderr, "Warning: encryption enabled but remote key not set, cryptographic authentication disabled.\n");
88 if (!connection_init_encryption(context, hsecretkey ? csecretkey : NULL, hpublickey ? cpublickey : NULL)) return false;
89 *keyupdate = true;
90 } else {
91 fprintf(stderr, "Warning: encryption disabled.\n");
92 }
93 if ((envval = getconf("PASSWORD"))) {
94 if (!connection_init_passwordauth(context, strdup(envval))) return false;
95 }
96 return true;
97 }
98
99 typedef union {
100 struct sockaddr any;
101 struct sockaddr_in ip4;
102 struct sockaddr_in6 ip6;
103 } sockaddr_any;
104
105 static int sockaddr_set_port(sockaddr_any* sa, int port) {
106 port = htons(port);
107 int af = sa->any.sa_family;
108 if (af == AF_INET) sa->ip4.sin_port = port;
109 else if (af == AF_INET6) sa->ip6.sin6_port = port;
110 else return errorexit("Unknown address family");
111 return 0;
112 }
113
114 static bool socket_init(connection_context* context) {
115 char* envval;
116 fprintf(stderr, "Initializing socket...\n");
117 struct addrinfo *ai_local = NULL, *ai_remote = NULL;
118 unsigned short af = 0;
119 int ret;
120 if ((envval = getconf("LOCAL_ADDRESS"))) {
121 if ((ret = getaddrinfo(envval, NULL, NULL, &ai_local))) return errorexitf("getaddrinfo(LOCAL_ADDRESS)", gai_strerror(ret));
122 if (!ai_local) return errorexit("LOCAL_ADDRESS lookup failed");
123 if (ai_local->ai_addrlen > sizeof(sockaddr_any)) return errorexit("Resolved LOCAL_ADDRESS is too big");
124 af = ai_local->ai_family;
125 }
126 if ((envval = getconf("REMOTE_ADDRESS"))) {
127 if ((ret = getaddrinfo(envval, NULL, NULL, &ai_remote))) return errorexitf("getaddrinfo(REMOTE_ADDRESS)", gai_strerror(ret));
128 if (!ai_remote) return errorexit("REMOTE_ADDRESS lookup failed");
129 if (ai_remote->ai_addrlen > sizeof(sockaddr_any)) return errorexit("Resolved REMOTE_ADDRESS is too big");
130 if (af && af != ai_remote->ai_family) return errorexit("Address families do not match");
131 af = ai_remote->ai_family;
132 }
133 if (!af) return connection_init_socket(context, 0, 1);
134 int sa_size = sizeof(sockaddr_any);
135 if (af == AF_INET) sa_size = sizeof(struct sockaddr_in);
136 else if (af == AF_INET6) sa_size = sizeof(struct sockaddr_in6);
137 int sfd = socket(af, SOCK_STREAM, IPPROTO_TCP);
138 if (sfd < 0) return errorexitp("Could not create socket");
139 sockaddr_any udpaddr;
140 if (ai_local) {
141 memset(&udpaddr, 0, sizeof(udpaddr));
142 udpaddr.any.sa_family = af;
143 memcpy(&udpaddr, ai_local->ai_addr, ai_local->ai_addrlen);
144 int port = 2998;
145 if ((envval = getconf("LOCAL_PORT"))) port = atoi(envval);
146 if (sockaddr_set_port(&udpaddr, port)) return -1;
147 if (bind(sfd, &udpaddr.any, sa_size)) return errorexitp("Could not bind socket");
148 }
149 if (ai_remote) {
150 memset(&udpaddr, 0, sizeof(udpaddr));
151 udpaddr.any.sa_family = af;
152 memcpy(&udpaddr, ai_remote->ai_addr, ai_remote->ai_addrlen);
153 int port = 2998;
154 if ((envval = getconf("REMOTE_PORT"))) port = atoi(envval);
155 if (sockaddr_set_port(&udpaddr, port)) return -1;
156 if (connect(sfd, &udpaddr.any, sa_size)) return errorexitp("Could not connect socket");
157 } else {
158 return errorexit("REMOTE_ADDRESS not specified and server mode is currently not supported :-(. Please use (x)inetd or similar.");
159 }
160 if (ai_local) freeaddrinfo(ai_local);
161 if (ai_remote) freeaddrinfo(ai_remote);
162 return connection_init_socket(context, sfd, sfd);
163 }
164
165 static bool mainA() {
166 connection_context context;
167 tunnel_context tunnel;
168 bool keyupdate = false;
169 if (!connection_init(&context)) return false;
170 if (!socket_init(&context)) return false;
171 if (!crypto_init(&context, &keyupdate)) return false;
172 if (!connection_init_done(&context)) return false;
173 while (!context.local_tunnelready) if (!connection_read(&context)) return false;
174 if (keyupdate) connection_update_key(&context);
175
176 if (!tunnel_init(&tunnel)) return false;
177 context.tunnel = &tunnel;
178 tunnel.connection = &context;
179
180 struct pollfd fds[2];
181 fds[0].fd = context.recv_socket;
182 fds[0].events = POLLIN;
183 fds[1].fd = tunnel.fd;
184 fds[1].events = POLLIN;
185
186 while (true) {
187 int len = poll(fds, 2, 10000);
188 if (len < 0) return errorexitp("poll failed");
189 else if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) return errorexit("poll error on socket");
190 else if (fds[1].revents & (POLLHUP | POLLNVAL)) return errorexit("poll error on tap device");
191 if (len == 0) {
192 if (keyupdate) {
193 if (!context.key_updated) return errorexit("key update timed out");
194 if (!connection_update_key(&context)) return false;
195 } else {
196 if (!connection_ping(&context)) return errorexit("ping timed out");
197 }
198 }
199 if (fds[0].revents & POLLERR) return errorexitp("poll error on socket");
200 if (fds[0].revents & POLLIN) if (!connection_read(&context)) return false;
201 if (fds[1].revents & POLLIN) if (!tunnel_read(&tunnel)) return false;
202 }
203 return -1;
204 }
205
206 int main() {
207 return mainA() ? 0 : -1;
208 }
209
210 int errorexit(const char* text) {
211 fprintf(stderr, "%s\n", text);
212 return false;
213 }
214 int errorexitf(const char* text, const char* error) {
215 fprintf(stderr, "%s: %s\n", text, error);
216 return false;
217 }
218 bool errorexitp(const char* text) {
219 perror(text);
220 return false;
221 }
222