Mercurial > hg > nnchat
comparison network.c @ 413:14b685cdbd2c
Rename files.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 24 May 2012 06:41:07 +0300 |
parents | libnnnet.c@3e64acb433e8 |
children | ac4862a94cd1 |
comparison
equal
deleted
inserted
replaced
412:3e64acb433e8 | 413:14b685cdbd2c |
---|---|
1 /* | |
2 * NNChat - Custom chat client for NewbieNudes.com chatrooms | |
3 * Written by Matti 'ccr' Hämäläinen | |
4 * (C) Copyright 2008-2012 Tecnic Software productions (TNSP) | |
5 */ | |
6 #include "network.h" | |
7 | |
8 | |
9 | |
10 #ifdef __WIN32 | |
11 const char *hstrerror(int err) | |
12 { | |
13 static char buf[64]; | |
14 snprintf(buf, sizeof(buf), "Error #%d", err); | |
15 return buf; | |
16 } | |
17 | |
18 | |
19 int nn_get_socket_errno(void) | |
20 { | |
21 return WSAGetLastError(); | |
22 } | |
23 | |
24 | |
25 const char *nn_get_socket_errstr(int err) | |
26 { | |
27 static char buf[64]; | |
28 switch (err) | |
29 { | |
30 case WSAEADDRINUSE: return "Address already in use"; | |
31 case WSAECONNABORTED: return "Software caused connection abort"; | |
32 case WSAECONNREFUSED: return "Connection refused"; | |
33 case WSAECONNRESET: return "Connection reset by peer"; | |
34 case WSAEHOSTUNREACH: return "No route to host"; | |
35 case WSAENETDOWN: return "Network is down"; | |
36 case WSAETIMEDOUT: return "Connection timed out"; | |
37 case WSAHOST_NOT_FOUND: return "Host not found"; | |
38 case WSAVERNOTSUPPORTED: return "Wrong WinSock DLL version"; | |
39 default: | |
40 snprintf(buf, sizeof(buf), "Error #%d", err); | |
41 return buf; | |
42 break; | |
43 } | |
44 } | |
45 #else | |
46 int nn_get_socket_errno(void) | |
47 { | |
48 return errno; | |
49 } | |
50 | |
51 | |
52 const char *nn_get_socket_errstr(int err) | |
53 { | |
54 return strerror(err); | |
55 } | |
56 #endif | |
57 | |
58 | |
59 void nn_conn_err(nn_conn_t *conn, const char *fmt, ...) | |
60 { | |
61 if (conn->errfunc != NULL) | |
62 { | |
63 va_list ap; | |
64 va_start(ap, fmt); | |
65 conn->errfunc(conn, fmt, ap); | |
66 va_end(ap); | |
67 } | |
68 } | |
69 | |
70 | |
71 static void nn_conn_msg(nn_conn_t *conn, const char *fmt, ...) | |
72 { | |
73 if (conn->msgfunc != NULL) | |
74 { | |
75 va_list ap; | |
76 va_start(ap, fmt); | |
77 conn->msgfunc(conn, fmt, ap); | |
78 va_end(ap); | |
79 } | |
80 } | |
81 | |
82 struct hostent *nn_resolve_host(nn_conn_t *conn, const char *name) | |
83 { | |
84 struct hostent *res = gethostbyname(name); | |
85 if (res == NULL) | |
86 nn_conn_err(conn, "Could not resolve hostname: %s\n", strerror(h_errno)); | |
87 else | |
88 nn_conn_msg(conn, "True hostname for %s is %s\n", name, res->h_name); | |
89 | |
90 return res; | |
91 } | |
92 | |
93 static const char *nn_proxy_types[] = | |
94 { | |
95 "none", | |
96 "SOCKS 4", | |
97 "SOCKS 4a", | |
98 NULL | |
99 }; | |
100 | |
101 | |
102 nn_conn_t * nn_conn_new( | |
103 void (*errfunc)(nn_conn_t *conn, const char *fmt, va_list ap), | |
104 void (*msgfunc)(nn_conn_t *conn, const char *fmt, va_list ap)) | |
105 { | |
106 nn_conn_t *conn = th_calloc(1, sizeof(nn_conn_t)); | |
107 | |
108 if (conn == NULL) | |
109 return NULL; | |
110 | |
111 conn->errfunc = errfunc; | |
112 conn->msgfunc = msgfunc; | |
113 | |
114 return conn; | |
115 } | |
116 | |
117 static BOOL nn_get_addr(struct in_addr *addr, struct hostent *hst) | |
118 { | |
119 if (hst != NULL) | |
120 { | |
121 *addr = *(struct in_addr *) (hst->h_addr); | |
122 return TRUE; | |
123 } | |
124 else | |
125 { | |
126 addr->s_addr = 0; | |
127 return FALSE; | |
128 } | |
129 } | |
130 | |
131 int nn_conn_set_proxy(nn_conn_t *conn, int type, int port, const char *host) | |
132 { | |
133 if (conn == NULL) | |
134 return -1; | |
135 | |
136 conn->proxy.type = type; | |
137 conn->proxy.port = port; | |
138 conn->proxy.host = th_strdup(host); | |
139 | |
140 if (host != NULL) | |
141 { | |
142 conn->proxy.hst = nn_resolve_host(conn, host); | |
143 nn_get_addr(&(conn->proxy.addr), conn->proxy.hst); | |
144 } | |
145 else | |
146 return -2; | |
147 | |
148 return 0; | |
149 } | |
150 | |
151 int nn_conn_open(nn_conn_t *conn, const int port, const char *host) | |
152 { | |
153 struct sockaddr_in dest; | |
154 static const char *userid = "James Bond"; | |
155 | |
156 if (conn == NULL) | |
157 return -1; | |
158 | |
159 conn->port = port; | |
160 if (host != NULL) | |
161 { | |
162 conn->host = th_strdup(host); | |
163 conn->hst = nn_resolve_host(conn, host); | |
164 } | |
165 | |
166 nn_get_addr(&(conn->addr), conn->hst); | |
167 | |
168 /* Prepare for connection */ | |
169 dest.sin_family = AF_INET; | |
170 | |
171 if (conn->proxy.type > NN_PROXY_NONE && conn->proxy.type < NN_PROXY_LAST) | |
172 { | |
173 dest.sin_port = htons(conn->proxy.port); | |
174 dest.sin_addr = conn->proxy.addr; | |
175 | |
176 nn_conn_msg(conn, "Connecting to %s proxy %s:%d ...\n", | |
177 nn_proxy_types[conn->proxy.type], | |
178 inet_ntoa(conn->proxy.addr), conn->proxy.port); | |
179 } | |
180 else | |
181 { | |
182 dest.sin_port = htons(conn->port); | |
183 dest.sin_addr = conn->addr; | |
184 | |
185 nn_conn_msg(conn, "Connecting to %s:%d ...\n", | |
186 inet_ntoa(conn->addr), conn->port); | |
187 } | |
188 | |
189 if ((conn->socket = socket(PF_INET, SOCK_STREAM, 0)) == -1) | |
190 { | |
191 conn->err = nn_get_socket_errno(); | |
192 nn_conn_err(conn, "Could not open socket: %s\n", nn_get_socket_errstr(conn->err)); | |
193 goto error; | |
194 } | |
195 | |
196 nn_conn_msg(conn, "Using socket %d.\n", conn->socket); | |
197 | |
198 if (connect(conn->socket, (struct sockaddr *) &dest, sizeof(dest)) == -1) | |
199 { | |
200 conn->err = nn_get_socket_errno(); | |
201 nn_conn_err(conn, "Could not connect: %s\n", nn_get_socket_errstr(conn->err)); | |
202 goto error; | |
203 } | |
204 | |
205 FD_ZERO(&(conn->sockfds)); | |
206 FD_SET(conn->socket, &(conn->sockfds)); | |
207 | |
208 /* Proxy-specific setup */ | |
209 if (conn->proxy.type == NN_PROXY_SOCKS4 || conn->proxy.type == NN_PROXY_SOCKS4A) | |
210 { | |
211 struct nn_socks_t *socksh; | |
212 size_t bufsiz = sizeof(struct nn_socks_t) + strlen(userid) + 1; | |
213 char *ptr, *buf; | |
214 int tries, status = -1; | |
215 | |
216 if (conn->proxy.type == NN_PROXY_SOCKS4A) | |
217 bufsiz += strlen(conn->host) + 1; | |
218 | |
219 ptr = buf = th_malloc(bufsiz); | |
220 if (buf == NULL) | |
221 { | |
222 conn->err = -1; | |
223 nn_conn_err(conn, "Could not allocate memory for SOCKS negotiation buffer, %d bytes.\n", bufsiz); | |
224 goto error; | |
225 } | |
226 | |
227 /* Create SOCKS 4/4A request */ | |
228 nn_conn_msg(conn, "Initializing proxy negotiation.\n"); | |
229 socksh = (struct nn_socks_t *) buf; | |
230 socksh->version = 4; | |
231 socksh->command = SOCKS_CMD_CONNECT; | |
232 socksh->port = htons(port); | |
233 if (conn->proxy.type == NN_PROXY_SOCKS4A) | |
234 socksh->addr = htonl(0x00000032); | |
235 else | |
236 socksh->addr = conn->addr.s_addr; | |
237 ptr += sizeof(struct nn_socks_t); | |
238 | |
239 strcpy(ptr, userid); | |
240 | |
241 if (conn->proxy.type == NN_PROXY_SOCKS4A) | |
242 { | |
243 ptr += strlen(userid) + 1; | |
244 strcpy(ptr, conn->host); | |
245 } | |
246 | |
247 /* Send request */ | |
248 nn_conn_reset(conn); | |
249 if (!nn_conn_send_buf(conn, buf, bufsiz)) | |
250 { | |
251 th_free(buf); | |
252 nn_conn_err(conn, "Error sending SOCKS proxy request.\n"); | |
253 goto error; | |
254 } | |
255 th_free(buf); | |
256 | |
257 /* Wait for SOCKS server to reply */ | |
258 for (status = tries = 1; tries <= 20 && status > 0; tries++) | |
259 { | |
260 #ifdef __WIN32 | |
261 Sleep(50); | |
262 #else | |
263 usleep(50000); | |
264 #endif | |
265 nn_conn_reset(conn); | |
266 status = nn_conn_pull(conn); | |
267 } | |
268 | |
269 /* Check results */ | |
270 if (status == 0) | |
271 { | |
272 struct nn_socks_res_t *res = (struct nn_socks_res_t *) &(conn->buf); | |
273 if (res->nb != 0) | |
274 { | |
275 nn_conn_err(conn, "Invalid SOCKS server reply, does not begin with NUL byte (%d).\n", res->nb); | |
276 goto error; | |
277 } | |
278 if (res->result != 0x5a) | |
279 { | |
280 char *s = NULL; | |
281 switch (res->result) | |
282 { | |
283 case 0x5b: s = "Request rejected or failed"; break; | |
284 case 0x5c: s = "Request failed because client is not running identd (or not reachable from the server)"; break; | |
285 case 0x5d: s = "Request failed because client's identd could not confirm the user ID string in the request"; break; | |
286 default: s = "Unknown SOCKS error response"; break; | |
287 } | |
288 nn_conn_err(conn, "SOCKS setup failed, 0x%02x: %s.\n", res->result, s); | |
289 goto error; | |
290 } | |
291 nn_conn_msg(conn, "SOCKS connection established!\n"); | |
292 } | |
293 else if (status < 0) | |
294 { | |
295 nn_conn_err(conn, "Proxy negotiation failed at try %d with network error: %d\n", tries, status); | |
296 goto error; | |
297 } | |
298 else | |
299 { | |
300 nn_conn_err(conn, "Proxy negotiation timed out.\n"); | |
301 goto error; | |
302 } | |
303 } | |
304 | |
305 nn_conn_reset(conn); | |
306 conn->status = NN_CONN_OPEN; | |
307 return 0; | |
308 | |
309 error: | |
310 conn->status = NN_CONN_CLOSED; | |
311 return -2; | |
312 } | |
313 | |
314 | |
315 void nn_conn_close(nn_conn_t *conn) | |
316 { | |
317 if (conn == NULL) | |
318 return; | |
319 | |
320 if (conn->socket >= 0) | |
321 { | |
322 #ifdef __WIN32 | |
323 closesocket(conn->socket); | |
324 #else | |
325 close(conn->socket); | |
326 #endif | |
327 conn->socket = -1; | |
328 } | |
329 | |
330 th_free(conn->host); | |
331 th_free(conn->proxy.host); | |
332 | |
333 conn->status = NN_CONN_CLOSED; | |
334 | |
335 th_free(conn); | |
336 } | |
337 | |
338 | |
339 BOOL nn_conn_send_buf(nn_conn_t *conn, const char *buf, const size_t len) | |
340 { | |
341 size_t bufLeft = len; | |
342 const char *bufPtr = buf; | |
343 | |
344 while (bufLeft > 0) | |
345 { | |
346 ssize_t bufSent; | |
347 bufSent = send(conn->socket, bufPtr, bufLeft, 0); | |
348 if (bufSent < 0) | |
349 { | |
350 conn->err = nn_get_socket_errno(); | |
351 nn_conn_err(conn, "nn_conn_send_buf() failed: %s", nn_get_socket_errstr(conn->err)); | |
352 return FALSE; | |
353 } | |
354 bufLeft -= bufSent; | |
355 bufPtr += bufSent; | |
356 } | |
357 | |
358 return TRUE; | |
359 } | |
360 | |
361 void nn_conn_reset(nn_conn_t *conn) | |
362 { | |
363 if (conn != NULL) | |
364 { | |
365 conn->ptr = conn->in_ptr = conn->buf; | |
366 conn->got_bytes = conn->total_bytes = 0; | |
367 } | |
368 } | |
369 | |
370 | |
371 int nn_conn_pull(nn_conn_t *conn) | |
372 { | |
373 int result; | |
374 struct timeval socktv; | |
375 fd_set tmpfds; | |
376 | |
377 if (conn == NULL) | |
378 return -10; | |
379 | |
380 /* Prod the input buffer */ | |
381 if (conn->in_ptr > conn->buf && conn->in_ptr - conn->ptr > 0) | |
382 { | |
383 size_t delta = conn->in_ptr - conn->ptr; | |
384 memmove(conn->buf, conn->in_ptr, delta); | |
385 conn->ptr = conn->buf; | |
386 conn->in_ptr -= delta; | |
387 conn->total_bytes -= delta; | |
388 } | |
389 | |
390 /* Check for incoming data */ | |
391 socktv.tv_sec = 0; | |
392 socktv.tv_usec = NN_DELAY_USEC; | |
393 tmpfds = conn->sockfds; | |
394 | |
395 if ((result = select(conn->socket + 1, &tmpfds, NULL, NULL, &socktv)) == -1) | |
396 { | |
397 conn->err = nn_get_socket_errno(); | |
398 if (conn->err != EINTR) | |
399 { | |
400 nn_conn_err(conn, "Error occured in select(%d, sockfds): %d, %s\n", | |
401 socket, conn->err, nn_get_socket_errstr(conn->err)); | |
402 return -1; | |
403 } | |
404 } | |
405 else if (FD_ISSET(conn->socket, &tmpfds)) | |
406 { | |
407 conn->got_bytes = recv(conn->socket, conn->in_ptr, NN_CONNBUF_SIZE - conn->total_bytes, 0); | |
408 if (conn->got_bytes < 0) | |
409 { | |
410 conn->err = nn_get_socket_errno(); | |
411 nn_conn_err(conn, "Error in recv: %d, %s\n", conn->err, nn_get_socket_errstr(conn->err)); | |
412 return -2; | |
413 } | |
414 else if (conn->got_bytes == 0) | |
415 { | |
416 nn_conn_err(conn, "Server closed connection.\n"); | |
417 conn->status = NN_CONN_CLOSED; | |
418 return -3; | |
419 } | |
420 else | |
421 { | |
422 conn->total_bytes += conn->got_bytes; | |
423 conn->in_ptr += conn->got_bytes; | |
424 return 0; | |
425 } | |
426 } | |
427 | |
428 return 1; | |
429 } | |
430 | |
431 | |
432 BOOL nn_conn_check(nn_conn_t *conn) | |
433 { | |
434 if (conn == NULL) | |
435 return FALSE; | |
436 | |
437 return conn->err == 0 && conn->status == NN_CONN_OPEN; | |
438 } | |
439 | |
440 | |
441 BOOL nn_network_init(void) | |
442 { | |
443 #ifdef __WIN32 | |
444 /* Initialize WinSock, if needed */ | |
445 WSADATA wsaData; | |
446 int err = WSAStartup(0x0101, &wsaData); | |
447 if (err != 0) | |
448 { | |
449 THERR("Could not initialize WinSock library (err=%d).\n", err); | |
450 return FALSE; | |
451 } | |
452 #endif | |
453 return TRUE; | |
454 } | |
455 | |
456 | |
457 void nn_network_close(void) | |
458 { | |
459 #ifdef __WIN32 | |
460 WSACleanup(); | |
461 #endif | |
462 } | |
463 | |
464 | |
465 BOOL nn_conn_buf_check(nn_conn_t *conn, size_t n) | |
466 { | |
467 return conn && conn->ptr && conn->in_ptr && (conn->ptr + n <= conn->in_ptr); | |
468 } | |
469 | |
470 | |
471 BOOL nn_conn_buf_skip(nn_conn_t *conn, size_t n) | |
472 { | |
473 if (nn_conn_buf_check(conn, n)) | |
474 { | |
475 conn->ptr += n; | |
476 return TRUE; | |
477 } | |
478 else | |
479 return FALSE; | |
480 } | |
481 | |
482 | |
483 int nn_conn_buf_strncmp(nn_conn_t *conn, const char *str, const size_t n) | |
484 { | |
485 int ret; | |
486 if (!nn_conn_buf_check(conn, n)) | |
487 return -1; | |
488 | |
489 if ((ret = strncmp(conn->ptr, str, n)) == 0) | |
490 { | |
491 conn->ptr += n; | |
492 return 0; | |
493 } | |
494 else | |
495 return ret; | |
496 } | |
497 | |
498 | |
499 int nn_conn_buf_strcmp(nn_conn_t *conn, const char *str) | |
500 { | |
501 return nn_conn_buf_strncmp(conn, str, strlen(str)); | |
502 } | |
503 | |
504 | |
505 char *nn_conn_buf_strstr(nn_conn_t *conn, const char *str) | |
506 { | |
507 char *pos; | |
508 size_t n = strlen(str); | |
509 | |
510 if (nn_conn_buf_check(conn, n) && ((pos = strstr(conn->ptr, str)) != NULL)) | |
511 { | |
512 conn->ptr = pos + n; | |
513 return pos; | |
514 } | |
515 else | |
516 return NULL; | |
517 } | |
518 | |
519 | |
520 BOOL nn_conn_send_msg(nn_conn_t *conn, const char *user, const char *fmt, ...) | |
521 { | |
522 char *tmp, *msg; | |
523 va_list ap; | |
524 | |
525 va_start(ap, fmt); | |
526 tmp = th_strdup_vprintf(fmt, ap); | |
527 va_end(ap); | |
528 | |
529 if (tmp == NULL) | |
530 return FALSE; | |
531 | |
532 msg = th_strdup_printf("<USER>%s</USER><MESSAGE>%s</MESSAGE>", user, tmp); | |
533 th_free(tmp); | |
534 | |
535 if (msg != NULL) | |
536 { | |
537 BOOL ret = nn_conn_send_buf(conn, msg, strlen(msg) + 1); | |
538 th_free(msg); | |
539 return ret; | |
540 } | |
541 else | |
542 return FALSE; | |
543 } |