| ---
tnetwork.c (43581B)
---
1 /************************************************************************
2 * network.c Low-level networking routines *
3 * Copyright (C) 1998-2021 Ben Webb *
4 * Email: benwebb@users.sf.net *
5 * WWW: https://dopewars.sourceforge.io/ *
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License *
9 * as published by the Free Software Foundation; either version 2 *
10 * of the License, or (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
20 * MA 02111-1307, USA. *
21 ************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 #include
25 #endif
26
27 #ifdef NETWORKING
28
29 #ifdef CYGWIN
30 #include /* For network functions */
31 #include /* For datatypes such as BOOL */
32 #include "winmain.h"
33 #else
34 #include /* For size_t etc. */
35 #include /* For struct sockaddr etc. */
36 #include /* For struct sockaddr_in etc. */
37 #include /* For socklen_t */
38 #include /* For getpwuid */
39 #include /* For memcpy, strlen etc. */
40 #ifdef HAVE_UNISTD_H
41 #include /* For close(), various types and
42 * constants */
43 #endif
44 #ifdef HAVE_FCNTL_H
45 #include /* For fcntl() */
46 #endif
47 #include /* For gethostbyname() */
48 #endif /* CYGWIN */
49
50 #include
51 #include /* For errno and Unix error codes */
52 #include /* For exit() and atoi() */
53 #include /* For perror() */
54
55 #include "error.h"
56 #include "network.h"
57 #include "nls.h"
58
59 /* Maximum sizes (in bytes) of read and write buffers - connections should
60 * be dropped if either buffer is filled */
61 #define MAXREADBUF (32768)
62 #define MAXWRITEBUF (65536)
63
64 /* SOCKS5 authentication method codes */
65 typedef enum {
66 SM_NOAUTH = 0, /* No authentication required */
67 SM_GSSAPI = 1, /* GSSAPI */
68 SM_USERPASSWD = 2 /* Username/password authentication */
69 } SocksMethods;
70
71 static gboolean StartSocksNegotiation(NetworkBuffer *NetBuf,
72 gchar *RemoteHost,
73 unsigned RemotePort);
74 static gboolean StartConnect(int *fd, const gchar *bindaddr, gchar *RemoteHost,
75 unsigned RemotePort, gboolean *doneOK,
76 LastError **error);
77
78 #ifdef CYGWIN
79
80 void StartNetworking()
81 {
82 WSADATA wsaData;
83 LastError *error;
84 GString *errstr;
85
86 if (WSAStartup(MAKEWORD(1, 0), &wsaData) != 0) {
87 error = NewError(ET_WINSOCK, WSAGetLastError(), NULL);
88 errstr = g_string_new("");
89 g_string_assign_error(errstr, error);
90 g_log(NULL, G_LOG_LEVEL_CRITICAL, _("Cannot initialize WinSock (%s)!"),
91 errstr->str);
92 g_string_free(errstr, TRUE);
93 FreeError(error);
94 exit(EXIT_FAILURE);
95 }
96 }
97
98 void StopNetworking()
99 {
100 WSACleanup();
101 }
102
103 void SetReuse(SOCKET sock)
104 {
105 BOOL i = TRUE;
106
107 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&i,
108 sizeof(i)) == -1) {
109 perror("setsockopt");
110 exit(EXIT_FAILURE);
111 }
112 }
113
114 void SetBlocking(SOCKET sock, gboolean blocking)
115 {
116 unsigned long param;
117
118 param = blocking ? 0 : 1;
119 ioctlsocket(sock, FIONBIO, ¶m);
120 }
121
122 #else
123
124 void StartNetworking()
125 {
126 }
127
128 void StopNetworking()
129 {
130 }
131
132 void SetReuse(int sock)
133 {
134 int i = 1;
135
136 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) == -1) {
137 perror("setsockopt");
138 exit(EXIT_FAILURE);
139 }
140 }
141
142 void SetBlocking(int sock, gboolean blocking)
143 {
144 fcntl(sock, F_SETFL, blocking ? 0 : O_NONBLOCK);
145 }
146
147 #endif /* CYGWIN */
148
149 static gboolean FinishConnect(int fd, LastError **error);
150
151 static void NetBufCallBack(NetworkBuffer *NetBuf, gboolean CallNow)
152 {
153 if (NetBuf && NetBuf->CallBack) {
154 (*NetBuf->CallBack) (NetBuf, NetBuf->status != NBS_PRECONNECT,
155 (NetBuf->status == NBS_CONNECTED
156 && NetBuf->WriteBuf.DataPresent)
157 || (NetBuf->status == NBS_SOCKSCONNECT
158 && NetBuf->negbuf.DataPresent)
159 || NetBuf->WaitConnect, TRUE, CallNow);
160 }
161 }
162
163 static void NetBufCallBackStop(NetworkBuffer *NetBuf)
164 {
165 if (NetBuf && NetBuf->CallBack) {
166 (*NetBuf->CallBack) (NetBuf, FALSE, FALSE, FALSE, FALSE);
167 }
168 }
169
170 static void InitConnBuf(ConnBuf *buf)
171 {
172 buf->Data = NULL;
173 buf->Length = 0;
174 buf->DataPresent = 0;
175 }
176
177 static void FreeConnBuf(ConnBuf *buf)
178 {
179 g_free(buf->Data);
180 InitConnBuf(buf);
181 }
182
183 /*
184 * Initializes the passed network buffer, ready for use. Messages sent
185 * or received on the buffered connection will be terminated by the
186 * given character, and if they end in "StripChar" it will be removed
187 * before the messages are sent or received.
188 */
189 void InitNetworkBuffer(NetworkBuffer *NetBuf, char Terminator,
190 char StripChar, SocksServer *socks)
191 {
192 NetBuf->fd = -1;
193 NetBuf->ioch = NULL;
194 NetBuf->InputTag = 0;
195 NetBuf->CallBack = NULL;
196 NetBuf->CallBackData = NULL;
197 NetBuf->Terminator = Terminator;
198 NetBuf->StripChar = StripChar;
199 InitConnBuf(&NetBuf->ReadBuf);
200 InitConnBuf(&NetBuf->WriteBuf);
201 InitConnBuf(&NetBuf->negbuf);
202 NetBuf->WaitConnect = FALSE;
203 NetBuf->status = NBS_PRECONNECT;
204 NetBuf->socks = socks;
205 NetBuf->host = NULL;
206 NetBuf->userpasswd = NULL;
207 NetBuf->error = NULL;
208 }
209
210 void SetNetworkBufferCallBack(NetworkBuffer *NetBuf, NBCallBack CallBack,
211 gpointer CallBackData)
212 {
213 NetBufCallBackStop(NetBuf);
214 NetBuf->CallBack = CallBack;
215 NetBuf->CallBackData = CallBackData;
216 NetBufCallBack(NetBuf, FALSE);
217 }
218
219 /*
220 * Sets the function used to obtain a username and password for SOCKS5
221 * username/password authentication.
222 */
223 void SetNetworkBufferUserPasswdFunc(NetworkBuffer *NetBuf,
224 NBUserPasswd userpasswd, gpointer data)
225 {
226 NetBuf->userpasswd = userpasswd;
227 NetBuf->userpasswddata = data;
228 }
229
230 /*
231 * Sets up the given network buffer to handle data being sent/received
232 * through the given socket.
233 */
234 void BindNetworkBufferToSocket(NetworkBuffer *NetBuf, int fd)
235 {
236 NetBuf->fd = fd;
237 #ifdef CYGIN
238 NetBuf->ioch = g_io_channel_win32_new_socket(fd);
239 #else
240 NetBuf->ioch = g_io_channel_unix_new(fd);
241 #endif
242
243 SetBlocking(fd, FALSE); /* We only deal with non-blocking sockets */
244 NetBuf->status = NBS_CONNECTED; /* Assume the socket is connected */
245 }
246
247 /*
248 * Returns TRUE if the pointer is to a valid network buffer, and it's
249 * connected to an active socket.
250 */
251 gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf)
252 {
253 return (NetBuf && NetBuf->fd >= 0);
254 }
255
256 gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,
257 const gchar *bindaddr,
258 gchar *RemoteHost, unsigned RemotePort)
259 {
260 gchar *realhost;
261 unsigned realport;
262 gboolean doneOK;
263
264 ShutdownNetworkBuffer(NetBuf);
265
266 if (NetBuf->socks) {
267 realhost = NetBuf->socks->name;
268 realport = NetBuf->socks->port;
269 } else {
270 realhost = RemoteHost;
271 realport = RemotePort;
272 }
273
274 if (StartConnect(&NetBuf->fd, bindaddr, realhost, realport, &doneOK,
275 &NetBuf->error)) {
276 #ifdef CYGIN
277 NetBuf->ioch = g_io_channel_win32_new_socket(NetBuf->fd);
278 #else
279 NetBuf->ioch = g_io_channel_unix_new(NetBuf->fd);
280 #endif
281 /* If we connected immediately, then set status, otherwise signal that
282 * we're waiting for the connect to complete */
283 if (doneOK) {
284 NetBuf->status = NetBuf->socks ? NBS_SOCKSCONNECT : NBS_CONNECTED;
285 NetBuf->sockstat = NBSS_METHODS;
286 } else {
287 NetBuf->WaitConnect = TRUE;
288 }
289
290 if (NetBuf->socks
291 && !StartSocksNegotiation(NetBuf, RemoteHost, RemotePort)) {
292 return FALSE;
293 }
294
295 /* Notify the owner if necessary to check for the connection
296 * completing and/or for data to be writeable */
297 NetBufCallBack(NetBuf, FALSE);
298
299 return TRUE;
300 } else {
301 return FALSE;
302 }
303 }
304
305 /*
306 * Frees the network buffer's data structures (leaving it in the
307 * 'initialized' state) and closes the accompanying socket.
308 */
309 void ShutdownNetworkBuffer(NetworkBuffer *NetBuf)
310 {
311 NetBufCallBackStop(NetBuf);
312
313 if (NetBuf->fd >= 0) {
314 CloseSocket(NetBuf->fd);
315 g_io_channel_unref(NetBuf->ioch);
316 NetBuf->fd = -1;
317 }
318
319 FreeConnBuf(&NetBuf->ReadBuf);
320 FreeConnBuf(&NetBuf->WriteBuf);
321 FreeConnBuf(&NetBuf->negbuf);
322
323 FreeError(NetBuf->error);
324 NetBuf->error = NULL;
325
326 g_free(NetBuf->host);
327
328 InitNetworkBuffer(NetBuf, NetBuf->Terminator, NetBuf->StripChar,
329 NetBuf->socks);
330 }
331
332 /*
333 * Updates the sets of read and write file descriptors to monitor
334 * input to/output from the given network buffer. MaxSock is updated
335 * with the highest-numbered file descriptor (plus 1) for use in a
336 * later select() call.
337 */
338 void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf, fd_set *readfds,
339 fd_set *writefds, fd_set *errorfds,
340 int *MaxSock)
341 {
342 if (!NetBuf || NetBuf->fd <= 0)
343 return;
344 FD_SET(NetBuf->fd, readfds);
345 if (errorfds)
346 FD_SET(NetBuf->fd, errorfds);
347 if (NetBuf->fd >= *MaxSock)
348 *MaxSock = NetBuf->fd + 1;
349 if ((NetBuf->status == NBS_CONNECTED && NetBuf->WriteBuf.DataPresent)
350 || (NetBuf->status == NBS_SOCKSCONNECT && NetBuf->negbuf.DataPresent)
351 || NetBuf->WaitConnect) {
352 FD_SET(NetBuf->fd, writefds);
353 }
354 }
355
356 typedef enum {
357 SEC_5FAILURE = 1,
358 SEC_5RULESET = 2,
359 SEC_5NETDOWN = 3,
360 SEC_5UNREACH = 4,
361 SEC_5CONNREF = 5,
362 SEC_5TTLEXPIRED = 6,
363 SEC_5COMMNOSUPP = 7,
364 SEC_5ADDRNOSUPP = 8,
365
366 SEC_REJECT = 91,
367 SEC_NOIDENTD = 92,
368 SEC_IDMISMATCH = 93,
369
370 SEC_UNKNOWN = 200,
371 SEC_AUTHFAILED,
372 SEC_USERCANCEL,
373 SEC_ADDRTYPE,
374 SEC_REPLYVERSION,
375 SEC_VERSION,
376 SEC_NOMETHODS
377 } SocksErrorCode;
378
379 static ErrTable SocksErrStr[] = {
380 /* SOCKS version 5 error messages */
381 {SEC_5FAILURE, N_("SOCKS server general failure")},
382 {SEC_5RULESET, N_("Connection denied by SOCKS ruleset")},
383 {SEC_5NETDOWN, N_("SOCKS: Network unreachable")},
384 {SEC_5UNREACH, N_("SOCKS: Host unreachable")},
385 {SEC_5CONNREF, N_("SOCKS: Connection refused")},
386 {SEC_5TTLEXPIRED, N_("SOCKS: TTL expired")},
387 {SEC_5COMMNOSUPP, N_("SOCKS: Command not supported")},
388 {SEC_5ADDRNOSUPP, N_("SOCKS: Address type not supported")},
389 {SEC_NOMETHODS, N_("SOCKS server rejected all offered methods")},
390 {SEC_ADDRTYPE, N_("Unknown SOCKS address type returned")},
391 {SEC_AUTHFAILED, N_("SOCKS authentication failed")},
392 {SEC_USERCANCEL, N_("SOCKS authentication canceled by user")},
393
394 /* SOCKS version 4 error messages */
395 {SEC_REJECT, N_("SOCKS: Request rejected or failed")},
396 {SEC_NOIDENTD, N_("SOCKS: Rejected - unable to contact identd")},
397 {SEC_IDMISMATCH,
398 N_("SOCKS: Rejected - identd reports different user-id")},
399
400 /* SOCKS errors due to protocol violations */
401 {SEC_UNKNOWN, N_("Unknown SOCKS reply code")},
402 {SEC_REPLYVERSION, N_("Unknown SOCKS reply version code")},
403 {SEC_VERSION, N_("Unknown SOCKS server version")},
404 {0, NULL}
405 };
406
407 static void SocksAppendError(GString *str, LastError *error)
408 {
409 LookupErrorCode(str, error->code, SocksErrStr, _("SOCKS error code %d"));
410 }
411
412 static ErrorType ETSocks = { SocksAppendError, NULL };
413
414 static gboolean Socks5UserPasswd(NetworkBuffer *NetBuf)
415 {
416 if (!NetBuf->userpasswd) {
417 SetError(&NetBuf->error, &ETSocks, SEC_NOMETHODS, NULL);
418 return FALSE;
419 } else {
420 /* Request a username and password (the callback function should in
421 * turn call SendSocks5UserPasswd when it's done) */
422 NetBuf->sockstat = NBSS_USERPASSWD;
423 (*NetBuf->userpasswd) (NetBuf, NetBuf->userpasswddata);
424 return TRUE;
425 }
426 }
427
428 void SendSocks5UserPasswd(NetworkBuffer *NetBuf, gchar *user,
429 gchar *password)
430 {
431 gchar *addpt;
432 guint addlen;
433 ConnBuf *conn;
434
435 if (!user || !password || !user[0] || !password[0]) {
436 SetError(&NetBuf->error, &ETSocks, SEC_USERCANCEL, NULL);
437 NetBufCallBack(NetBuf, TRUE);
438 return;
439 }
440 conn = &NetBuf->negbuf;
441 addlen = 3 + strlen(user) + strlen(password);
442 addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
443 if (!addpt || strlen(user) > 255 || strlen(password) > 255) {
444 SetError(&NetBuf->error, ET_CUSTOM, E_FULLBUF, NULL);
445 NetBufCallBack(NetBuf, TRUE);
446 return;
447 }
448 addpt[0] = 1; /* Subnegotiation version code */
449 addpt[1] = strlen(user);
450 strcpy(&addpt[2], user);
451 addpt[2 + strlen(user)] = strlen(password);
452 strcpy(&addpt[3 + strlen(user)], password);
453
454 CommitWriteBuffer(NetBuf, conn, addpt, addlen);
455 }
456
457 static gboolean Socks5Connect(NetworkBuffer *NetBuf)
458 {
459 gchar *addpt;
460 guint addlen, hostlen;
461 ConnBuf *conn;
462 unsigned short int netport;
463
464 conn = &NetBuf->negbuf;
465 g_assert(NetBuf->host);
466 hostlen = strlen(NetBuf->host);
467 if (hostlen > 255)
468 return FALSE;
469
470 netport = htons(NetBuf->port);
471 g_assert(sizeof(netport) == 2);
472
473 addlen = hostlen + 7;
474 addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
475 if (!addpt)
476 return FALSE;
477 addpt[0] = 5; /* SOCKS version 5 */
478 addpt[1] = 1; /* CONNECT */
479 addpt[2] = 0; /* reserved - must be zero */
480 addpt[3] = 3; /* Address type - FQDN */
481 addpt[4] = hostlen; /* Length of address */
482 strcpy(&addpt[5], NetBuf->host);
483 memcpy(&addpt[5 + hostlen], &netport, sizeof(netport));
484
485 NetBuf->sockstat = NBSS_CONNECT;
486
487 CommitWriteBuffer(NetBuf, conn, addpt, addlen);
488
489 return TRUE;
490 }
491
492 static gboolean HandleSocksReply(NetworkBuffer *NetBuf)
493 {
494 gchar *data;
495 guchar addrtype;
496 guint replylen;
497 gboolean retval = TRUE;
498
499 if (NetBuf->socks->version == 5) {
500 if (NetBuf->sockstat == NBSS_METHODS) {
501 data = GetWaitingData(NetBuf, 2);
502 if (data) {
503 retval = FALSE;
504 if (data[0] != 5) {
505 SetError(&NetBuf->error, &ETSocks, SEC_VERSION, NULL);
506 } else if (data[1] == SM_NOAUTH) {
507 retval = Socks5Connect(NetBuf);
508 } else if (data[1] == SM_USERPASSWD) {
509 retval = Socks5UserPasswd(NetBuf);
510 } else {
511 SetError(&NetBuf->error, &ETSocks, SEC_NOMETHODS, NULL);
512 }
513 g_free(data);
514 }
515 } else if (NetBuf->sockstat == NBSS_USERPASSWD) {
516 data = GetWaitingData(NetBuf, 2);
517 if (data) {
518 retval = FALSE;
519 if (data[1] != 0) {
520 SetError(&NetBuf->error, &ETSocks, SEC_AUTHFAILED, NULL);
521 } else {
522 retval = Socks5Connect(NetBuf);
523 }
524 g_free(data);
525 }
526 } else if (NetBuf->sockstat == NBSS_CONNECT) {
527 data = PeekWaitingData(NetBuf, 5);
528 if (data) {
529 retval = FALSE;
530 addrtype = data[3];
531 if (data[0] != 5) {
532 SetError(&NetBuf->error, &ETSocks, SEC_VERSION, NULL);
533 } else if (data[1] > 8) {
534 SetError(&NetBuf->error, &ETSocks, SEC_UNKNOWN, NULL);
535 } else if (data[1] != 0) {
536 SetError(&NetBuf->error, &ETSocks, data[1], NULL);
537 } else if (addrtype != 1 && addrtype != 3 && addrtype != 4) {
538 SetError(&NetBuf->error, &ETSocks, SEC_ADDRTYPE, NULL);
539 } else {
540 retval = TRUE;
541 replylen = 6;
542 if (addrtype == 1)
543 replylen += 4; /* IPv4 address */
544 else if (addrtype == 4)
545 replylen += 16; /* IPv6 address */
546 else
547 replylen += data[4]; /* FQDN */
548 data = GetWaitingData(NetBuf, replylen);
549 if (data) {
550 NetBuf->status = NBS_CONNECTED;
551 g_free(data);
552 NetBufCallBack(NetBuf, FALSE); /* status has changed */
553 }
554 }
555 }
556 }
557 return retval;
558 } else {
559 data = GetWaitingData(NetBuf, 8);
560 if (data) {
561 retval = FALSE;
562 if (data[0] != 0) {
563 SetError(&NetBuf->error, &ETSocks, SEC_REPLYVERSION, NULL);
564 } else {
565 if (data[1] == 90) {
566 NetBuf->status = NBS_CONNECTED;
567 NetBufCallBack(NetBuf, FALSE); /* status has changed */
568 retval = TRUE;
569 } else if (data[1] >= SEC_REJECT && data[1] <= SEC_IDMISMATCH) {
570 SetError(&NetBuf->error, &ETSocks, data[1], NULL);
571 } else {
572 SetError(&NetBuf->error, &ETSocks, SEC_UNKNOWN, NULL);
573 }
574 }
575 g_free(data);
576 }
577 return retval;
578 }
579 }
580
581 /*
582 * Reads and writes data if the network connection is ready. Sets the
583 * various OK variables to TRUE if no errors occurred in the relevant
584 * operations, and returns TRUE if data was read and is waiting for
585 * processing.
586 */
587 static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,
588 gboolean ReadReady,
589 gboolean WriteReady,
590 gboolean ErrorReady, gboolean *ReadOK,
591 gboolean *WriteOK, gboolean *ErrorOK)
592 {
593 gboolean DataWaiting = FALSE, ConnectDone = FALSE;
594 gboolean retval;
595
596 *ReadOK = *WriteOK = *ErrorOK = TRUE;
597
598 if (ErrorReady || NetBuf->error)
599 *ErrorOK = FALSE;
600 else if (NetBuf->WaitConnect) {
601 if (WriteReady) {
602 retval = FinishConnect(NetBuf->fd, &NetBuf->error);
603 ConnectDone = TRUE;
604 NetBuf->WaitConnect = FALSE;
605
606 if (retval) {
607 if (NetBuf->socks) {
608 NetBuf->status = NBS_SOCKSCONNECT;
609 NetBuf->sockstat = NBSS_METHODS;
610 } else {
611 NetBuf->status = NBS_CONNECTED;
612 }
613 } else {
614 NetBuf->status = NBS_PRECONNECT;
615 *WriteOK = FALSE;
616 }
617 }
618 } else {
619 if (WriteReady)
620 *WriteOK = WriteDataToWire(NetBuf);
621
622 if (ReadReady) {
623 *ReadOK = ReadDataFromWire(NetBuf);
624 if (NetBuf->ReadBuf.DataPresent > 0 &&
625 NetBuf->status == NBS_SOCKSCONNECT) {
626 if (!HandleSocksReply(NetBuf)
627 || NetBuf->error) { /* From SendSocks5UserPasswd, possibly */
628 *ErrorOK = FALSE;
629 }
630 }
631 if (NetBuf->ReadBuf.DataPresent > 0 &&
632 NetBuf->status != NBS_SOCKSCONNECT) {
633 DataWaiting = TRUE;
634 }
635 }
636 }
637
638 if (!(*ErrorOK && *WriteOK && *ReadOK)) {
639 /* We don't want to check the socket any more */
640 NetBufCallBackStop(NetBuf);
641 /* If there were errors, then the socket is now useless - so close it */
642 CloseSocket(NetBuf->fd);
643 NetBuf->fd = -1;
644 } else if (ConnectDone) {
645 /* If we just connected, then no need to listen for write-ready status
646 * any more */
647 NetBufCallBack(NetBuf, FALSE);
648 } else if (WriteReady
649 && ((NetBuf->status == NBS_CONNECTED
650 && NetBuf->WriteBuf.DataPresent == 0)
651 || (NetBuf->status == NBS_SOCKSCONNECT
652 && NetBuf->negbuf.DataPresent == 0))) {
653 /* If we wrote out everything, then tell the owner so that the socket
654 * no longer needs to be checked for write-ready status */
655 NetBufCallBack(NetBuf, FALSE);
656 }
657
658 return DataWaiting;
659 }
660
661 /*
662 * Responds to a select() call by reading/writing data as necessary.
663 * If any data were read, TRUE is returned. "DoneOK" is set TRUE
664 * unless a fatal error (i.e. the connection was broken) occurred.
665 */
666 gboolean RespondToSelect(NetworkBuffer *NetBuf, fd_set *readfds,
667 fd_set *writefds, fd_set *errorfds,
668 gboolean *DoneOK)
669 {
670 gboolean ReadOK, WriteOK, ErrorOK;
671 gboolean DataWaiting = FALSE;
672
673 *DoneOK = TRUE;
674 if (!NetBuf || NetBuf->fd <= 0)
675 return DataWaiting;
676 DataWaiting = DoNetworkBufferStuff(NetBuf, FD_ISSET(NetBuf->fd, readfds),
677 FD_ISSET(NetBuf->fd, writefds),
678 errorfds ? FD_ISSET(NetBuf->fd,
679 errorfds) : FALSE,
680 &ReadOK, &WriteOK, &ErrorOK);
681 *DoneOK = (WriteOK && ErrorOK && ReadOK);
682 return DataWaiting;
683 }
684
685 gboolean NetBufHandleNetwork(NetworkBuffer *NetBuf, gboolean ReadReady,
686 gboolean WriteReady, gboolean ErrorReady,
687 gboolean *DoneOK)
688 {
689 gboolean ReadOK, WriteOK, ErrorOK;
690 gboolean DataWaiting = FALSE;
691
692 *DoneOK = TRUE;
693 if (!NetBuf || NetBuf->fd <= 0)
694 return DataWaiting;
695
696 DataWaiting = DoNetworkBufferStuff(NetBuf, ReadReady, WriteReady, ErrorReady,
697 &ReadOK, &WriteOK, &ErrorOK);
698
699 *DoneOK = (WriteOK && ErrorOK && ReadOK);
700 return DataWaiting;
701 }
702
703 /*
704 * Returns the number of complete (terminated) messages waiting in the
705 * given network buffer. This is the number of times that
706 * GetWaitingMessage() can be safely called without it returning NULL.
707 */
708 gint CountWaitingMessages(NetworkBuffer *NetBuf)
709 {
710 ConnBuf *conn;
711 gint i, msgs = 0;
712
713 if (NetBuf->status != NBS_CONNECTED)
714 return 0;
715
716 conn = &NetBuf->ReadBuf;
717
718 if (conn->Data)
719 for (i = 0; i < conn->DataPresent; i++) {
720 if (conn->Data[i] == NetBuf->Terminator)
721 msgs++;
722 }
723 return msgs;
724 }
725
726 gchar *PeekWaitingData(NetworkBuffer *NetBuf, int numbytes)
727 {
728 ConnBuf *conn;
729
730 conn = &NetBuf->ReadBuf;
731 if (!conn->Data || conn->DataPresent < numbytes)
732 return NULL;
733 else
734 return conn->Data;
735 }
736
737 gchar *GetWaitingData(NetworkBuffer *NetBuf, int numbytes)
738 {
739 ConnBuf *conn;
740 gchar *data;
741
742 conn = &NetBuf->ReadBuf;
743 if (!conn->Data || conn->DataPresent < numbytes)
744 return NULL;
745
746 data = g_new(gchar, numbytes);
747 memcpy(data, conn->Data, numbytes);
748
749 memmove(&conn->Data[0], &conn->Data[numbytes],
750 conn->DataPresent - numbytes);
751 conn->DataPresent -= numbytes;
752
753 return data;
754 }
755
756 /*
757 * Reads a complete (terminated) message from the network buffer. The
758 * message is removed from the buffer, and returned as a null-terminated
759 * string (the network terminator is removed). If no complete message is
760 * waiting, NULL is returned. The string is dynamically allocated, and
761 * so must be g_free'd by the caller.
762 */
763 gchar *GetWaitingMessage(NetworkBuffer *NetBuf)
764 {
765 ConnBuf *conn;
766 int MessageLen;
767 char *SepPt;
768 gchar *NewMessage;
769
770 conn = &NetBuf->ReadBuf;
771 if (!conn->Data || !conn->DataPresent || NetBuf->status != NBS_CONNECTED) {
772 return NULL;
773 }
774 SepPt = memchr(conn->Data, NetBuf->Terminator, conn->DataPresent);
775 if (!SepPt)
776 return NULL;
777 *SepPt = '\0';
778 MessageLen = SepPt - conn->Data + 1;
779 SepPt--;
780 if (NetBuf->StripChar && *SepPt == NetBuf->StripChar)
781 *SepPt = '\0';
782 NewMessage = g_new(gchar, MessageLen);
783
784 memcpy(NewMessage, conn->Data, MessageLen);
785 if (MessageLen < conn->DataPresent) {
786 memmove(&conn->Data[0], &conn->Data[MessageLen],
787 conn->DataPresent - MessageLen);
788 }
789 conn->DataPresent -= MessageLen;
790 return NewMessage;
791 }
792
793 /*
794 * Reads any waiting data on the given network buffer's TCP/IP connection
795 * into the read buffer. Returns FALSE if the connection was closed, or
796 * if the read buffer's maximum size was reached.
797 */
798 gboolean ReadDataFromWire(NetworkBuffer *NetBuf)
799 {
800 ConnBuf *conn;
801 int CurrentPosition, BytesRead;
802
803 conn = &NetBuf->ReadBuf;
804 CurrentPosition = conn->DataPresent;
805 while (1) {
806 if (CurrentPosition >= conn->Length) {
807 if (conn->Length == MAXREADBUF) {
808 SetError(&NetBuf->error, ET_CUSTOM, E_FULLBUF, NULL);
809 return FALSE; /* drop connection */
810 }
811 if (conn->Length == 0)
812 conn->Length = 256;
813 else
814 conn->Length *= 2;
815 if (conn->Length > MAXREADBUF)
816 conn->Length = MAXREADBUF;
817 conn->Data = g_realloc(conn->Data, conn->Length);
818 }
819 BytesRead = recv(NetBuf->fd, &conn->Data[CurrentPosition],
820 conn->Length - CurrentPosition, 0);
821 if (BytesRead == SOCKET_ERROR) {
822 #ifdef CYGWIN
823 int Error = WSAGetLastError();
824
825 if (Error == WSAEWOULDBLOCK)
826 break;
827 else {
828 SetError(&NetBuf->error, ET_WINSOCK, Error, NULL);
829 return FALSE;
830 }
831 #else
832 if (errno == EAGAIN)
833 break;
834 else if (errno != EINTR) {
835 SetError(&NetBuf->error, ET_ERRNO, errno, NULL);
836 return FALSE;
837 }
838 #endif
839 } else if (BytesRead == 0) {
840 return FALSE;
841 } else {
842 CurrentPosition += BytesRead;
843 conn->DataPresent = CurrentPosition;
844 }
845 }
846 return TRUE;
847 }
848
849 gchar *ExpandWriteBuffer(ConnBuf *conn, int numbytes, LastError **error)
850 {
851 int newlen;
852
853 newlen = conn->DataPresent + numbytes;
854 if (newlen > conn->Length) {
855 conn->Length *= 2;
856 conn->Length = MAX(conn->Length, newlen);
857 if (conn->Length > MAXWRITEBUF)
858 conn->Length = MAXWRITEBUF;
859 if (newlen > conn->Length) {
860 if (error)
861 SetError(error, ET_CUSTOM, E_FULLBUF, NULL);
862 return NULL;
863 }
864 conn->Data = g_realloc(conn->Data, conn->Length);
865 }
866
867 return (&conn->Data[conn->DataPresent]);
868 }
869
870 void CommitWriteBuffer(NetworkBuffer *NetBuf, ConnBuf *conn,
871 gchar *addpt, guint addlen)
872 {
873 conn->DataPresent += addlen;
874
875 /* If the buffer was empty before, we may need to tell the owner to
876 * check the socket for write-ready status */
877 if (NetBuf && addpt == conn->Data)
878 NetBufCallBack(NetBuf, FALSE);
879 }
880
881 /*
882 * Writes the null-terminated string "data" to the network buffer, ready
883 * to be sent to the wire when the network connection becomes free. The
884 * message is automatically terminated. Fails to write the message without
885 * error if the buffer reaches its maximum size (although this error will
886 * be detected when an attempt is made to write the buffer to the wire).
887 */
888 void QueueMessageForSend(NetworkBuffer *NetBuf, gchar *data)
889 {
890 gchar *addpt;
891 guint addlen;
892 ConnBuf *conn;
893
894 conn = &NetBuf->WriteBuf;
895
896 if (!data)
897 return;
898 addlen = strlen(data) + 1;
899 addpt = ExpandWriteBuffer(conn, addlen, NULL);
900 if (!addpt)
901 return;
902
903 memcpy(addpt, data, addlen);
904 addpt[addlen - 1] = NetBuf->Terminator;
905
906 CommitWriteBuffer(NetBuf, conn, addpt, addlen);
907 }
908
909 static void SetNetworkError(LastError **error) {
910 #ifdef CYGWIN
911 SetError(error, ET_WINSOCK, WSAGetLastError(), NULL);
912 #else
913 SetError(error, ET_HERRNO, h_errno, NULL);
914 #endif
915 }
916
917 static struct hostent *LookupHostname(const gchar *host, LastError **error)
918 {
919 struct hostent *he;
920
921 if ((he = gethostbyname(host)) == NULL && error) {
922 SetNetworkError(error);
923 }
924 return he;
925 }
926
927 gboolean StartSocksNegotiation(NetworkBuffer *NetBuf, gchar *RemoteHost,
928 unsigned RemotePort)
929 {
930 guint num_methods;
931 ConnBuf *conn;
932 struct hostent *he;
933 gchar *addpt;
934 guint addlen, i;
935 struct in_addr *haddr;
936 unsigned short int netport;
937 gchar *username = NULL;
938
939 #ifdef CYGWIN
940 DWORD bufsize;
941 #else
942 struct passwd *pwd;
943 #endif
944
945 conn = &NetBuf->negbuf;
946
947 if (NetBuf->socks->version == 5) {
948 num_methods = 1;
949 if (NetBuf->userpasswd)
950 num_methods++;
951 addlen = 2 + num_methods;
952 addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
953 if (!addpt)
954 return FALSE;
955 addpt[0] = 5; /* SOCKS version 5 */
956 addpt[1] = num_methods;
957 i = 2;
958 addpt[i++] = SM_NOAUTH;
959 if (NetBuf->userpasswd)
960 addpt[i++] = SM_USERPASSWD;
961
962 g_free(NetBuf->host);
963 NetBuf->host = g_strdup(RemoteHost);
964 NetBuf->port = RemotePort;
965
966 CommitWriteBuffer(NetBuf, conn, addpt, addlen);
967
968 return TRUE;
969 }
970
971 he = LookupHostname(RemoteHost, &NetBuf->error);
972 if (!he)
973 return FALSE;
974
975 if (NetBuf->socks->user && NetBuf->socks->user[0]) {
976 username = g_strdup(NetBuf->socks->user);
977 } else {
978 #ifdef CYGWIN
979 bufsize = 0;
980 WNetGetUser(NULL, username, &bufsize);
981 if (GetLastError() != ERROR_MORE_DATA) {
982 SetError(&NetBuf->error, ET_WIN32, GetLastError(), NULL);
983 return FALSE;
984 } else {
985 username = g_malloc(bufsize);
986 if (WNetGetUser(NULL, username, &bufsize) != NO_ERROR) {
987 SetError(&NetBuf->error, ET_WIN32, GetLastError(), NULL);
988 return FALSE;
989 }
990 }
991 #else
992 if (NetBuf->socks->numuid) {
993 username = g_strdup_printf("%d", getuid());
994 } else {
995 pwd = getpwuid(getuid());
996 if (!pwd || !pwd->pw_name)
997 return FALSE;
998 username = g_strdup(pwd->pw_name);
999 }
1000 #endif
1001 }
1002 addlen = 9 + strlen(username);
1003
1004 haddr = (struct in_addr *)he->h_addr;
1005 g_assert(sizeof(struct in_addr) == 4);
1006
1007 netport = htons(RemotePort);
1008 g_assert(sizeof(netport) == 2);
1009
1010 addpt = ExpandWriteBuffer(conn, addlen, &NetBuf->error);
1011 if (!addpt)
1012 return FALSE;
1013
1014 addpt[0] = 4; /* SOCKS version */
1015 addpt[1] = 1; /* CONNECT */
1016 memcpy(&addpt[2], &netport, sizeof(netport));
1017 memcpy(&addpt[4], haddr, sizeof(struct in_addr));
1018 strcpy(&addpt[8], username);
1019 g_free(username);
1020 addpt[addlen - 1] = '\0';
1021
1022 CommitWriteBuffer(NetBuf, conn, addpt, addlen);
1023
1024 return TRUE;
1025 }
1026
1027 static gboolean WriteBufToWire(NetworkBuffer *NetBuf, ConnBuf *conn)
1028 {
1029 int CurrentPosition, BytesSent;
1030
1031 if (!conn->Data || !conn->DataPresent)
1032 return TRUE;
1033 if (conn->Length == MAXWRITEBUF) {
1034 SetError(&NetBuf->error, ET_CUSTOM, E_FULLBUF, NULL);
1035 return FALSE;
1036 }
1037 CurrentPosition = 0;
1038 while (CurrentPosition < conn->DataPresent) {
1039 BytesSent = send(NetBuf->fd, &conn->Data[CurrentPosition],
1040 conn->DataPresent - CurrentPosition, 0);
1041 if (BytesSent == SOCKET_ERROR) {
1042 #ifdef CYGWIN
1043 int Error = WSAGetLastError();
1044
1045 if (Error == WSAEWOULDBLOCK)
1046 break;
1047 else {
1048 SetError(&NetBuf->error, ET_WINSOCK, Error, NULL);
1049 return FALSE;
1050 }
1051 #else
1052 if (errno == EAGAIN)
1053 break;
1054 else if (errno != EINTR) {
1055 SetError(&NetBuf->error, ET_ERRNO, errno, NULL);
1056 return FALSE;
1057 }
1058 #endif
1059 } else {
1060 CurrentPosition += BytesSent;
1061 }
1062 }
1063 if (CurrentPosition > 0 && CurrentPosition < conn->DataPresent) {
1064 memmove(&conn->Data[0], &conn->Data[CurrentPosition],
1065 conn->DataPresent - CurrentPosition);
1066 }
1067 conn->DataPresent -= CurrentPosition;
1068 return TRUE;
1069 }
1070
1071 /*
1072 * Writes any waiting data in the network buffer to the wire. Returns
1073 * TRUE on success, or FALSE if the buffer's maximum length is
1074 * reached, or the remote end has closed the connection.
1075 */
1076 gboolean WriteDataToWire(NetworkBuffer *NetBuf)
1077 {
1078 if (NetBuf->status == NBS_SOCKSCONNECT) {
1079 return WriteBufToWire(NetBuf, &NetBuf->negbuf);
1080 } else {
1081 return WriteBufToWire(NetBuf, &NetBuf->WriteBuf);
1082 }
1083 }
1084
1085 static size_t MetaConnWriteFunc(void *contents, size_t size, size_t nmemb,
1086 void *userp)
1087 {
1088 size_t realsize = size * nmemb;
1089 CurlConnection *conn = (CurlConnection *)userp;
1090
1091 conn->data = g_realloc(conn->data, conn->data_size + realsize + 1);
1092 memcpy(&(conn->data[conn->data_size]), contents, realsize);
1093 conn->data_size += realsize;
1094 conn->data[conn->data_size] = 0;
1095
1096 return realsize;
1097 }
1098
1099 static size_t MetaConnHeaderFunc(char *contents, size_t size, size_t nmemb,
1100 void *userp)
1101 {
1102 size_t realsize = size * nmemb;
1103 CurlConnection *conn = (CurlConnection *)userp;
1104
1105 gchar *str = g_strchomp(g_strndup(contents, realsize));
1106 g_ptr_array_add(conn->headers, (gpointer)str);
1107 return realsize;
1108 }
1109
1110 void CurlInit(CurlConnection *conn)
1111 {
1112 curl_global_init(CURL_GLOBAL_DEFAULT);
1113 conn->multi = curl_multi_init();
1114 conn->h = curl_easy_init();
1115 conn->running = FALSE;
1116 conn->Terminator = '\n';
1117 conn->StripChar = '\r';
1118 conn->data_size = 0;
1119 conn->headers = NULL;
1120 conn->timer_cb = NULL;
1121 conn->socket_cb = NULL;
1122 }
1123
1124 void CloseCurlConnection(CurlConnection *conn)
1125 {
1126 if (conn->running) {
1127 curl_multi_remove_handle(conn->multi, conn->h);
1128 g_free(conn->data);
1129 conn->data_size = 0;
1130 conn->running = FALSE;
1131 g_ptr_array_free(conn->headers, TRUE);
1132 conn->headers = NULL;
1133 }
1134 }
1135
1136 void CurlCleanup(CurlConnection *conn)
1137 {
1138 if (conn->running) {
1139 CloseCurlConnection(conn);
1140 }
1141 curl_easy_cleanup(conn->h);
1142 curl_multi_cleanup(conn->multi);
1143 curl_global_cleanup();
1144 }
1145
1146 gboolean HandleCurlMultiReturn(CurlConnection *conn, CURLMcode mres,
1147 GError **err)
1148 {
1149 struct CURLMsg *m;
1150 if (mres != CURLM_OK && mres != CURLM_CALL_MULTI_PERFORM) {
1151 CloseCurlConnection(conn);
1152 g_set_error_literal(err, DOPE_CURLM_ERROR, mres, curl_multi_strerror(mres));
1153 return FALSE;
1154 }
1155
1156 do {
1157 int msgq = 0;
1158 m = curl_multi_info_read(conn->multi, &msgq);
1159 if (m && m->msg == CURLMSG_DONE && m->data.result != CURLE_OK) {
1160 CloseCurlConnection(conn);
1161 g_set_error_literal(err, DOPE_CURL_ERROR, m->data.result,
1162 curl_easy_strerror(m->data.result));
1163 return FALSE;
1164 }
1165 } while(m);
1166
1167 return TRUE;
1168 }
1169
1170 gboolean CurlConnectionPerform(CurlConnection *conn, int *still_running,
1171 GError **err)
1172 {
1173 CURLMcode mres = curl_multi_perform(conn->multi, still_running);
1174 return HandleCurlMultiReturn(conn, mres, err);
1175 }
1176
1177 gboolean CurlConnectionSocketAction(CurlConnection *conn, curl_socket_t fd,
1178 int action, int *still_running,
1179 GError **err)
1180 {
1181 CURLMcode mres = curl_multi_socket_action(conn->multi, fd, action,
1182 still_running);
1183 return HandleCurlMultiReturn(conn, mres, err);
1184 }
1185
1186 GQuark dope_curl_error_quark(void)
1187 {
1188 return g_quark_from_static_string("dope-curl-error-quark");
1189 }
1190
1191 GQuark dope_curlm_error_quark(void)
1192 {
1193 return g_quark_from_static_string("dope-curlm-error-quark");
1194 }
1195
1196 gboolean CurlEasySetopt1(CURL *curl, CURLoption option, void *arg, GError **err)
1197 {
1198 CURLcode res = curl_easy_setopt(curl, option, arg);
1199 if (res == CURLE_OK) {
1200 return TRUE;
1201 } else {
1202 g_set_error_literal(err, DOPE_CURL_ERROR, res, curl_easy_strerror(res));
1203 return FALSE;
1204 }
1205 }
1206
1207 #ifdef CYGWIN
1208 /* Set the path to TLS CA certificates. Without this, curl connections
1209 to the metaserver may fail on Windows as it cannot verify the
1210 certificate.
1211 */
1212 static gboolean SetCaInfo(CurlConnection *conn, GError **err)
1213 {
1214 gchar *bindir, *cainfo;
1215 gboolean ret;
1216
1217 /* Point to a .crt file in the same directory as dopewars.exe */
1218 bindir = GetBinaryDir();
1219 cainfo = g_strdup_printf("%s\\ca-bundle.crt", bindir);
1220 g_free(bindir);
1221
1222 ret = CurlEasySetopt1(conn->h, CURLOPT_CAINFO, cainfo, err);
1223 g_free(cainfo);
1224 return ret;
1225 }
1226 #endif
1227
1228 gboolean OpenCurlConnection(CurlConnection *conn, char *URL, char *body,
1229 GError **err)
1230 {
1231 /* If the previous connect hung for so long that it's still active, then
1232 * break the connection before we start a new one */
1233 if (conn->running) {
1234 CloseCurlConnection(conn);
1235 }
1236
1237 if (conn->h) {
1238 int still_running;
1239 CURLMcode mres;
1240 if (body && !CurlEasySetopt1(conn->h, CURLOPT_COPYPOSTFIELDS, body, err)) {
1241 return FALSE;
1242 }
1243
1244 if (!CurlEasySetopt1(conn->h, CURLOPT_URL, URL, err)
1245 || !CurlEasySetopt1(conn->h, CURLOPT_WRITEFUNCTION, MetaConnWriteFunc,
1246 err)
1247 || !CurlEasySetopt1(conn->h, CURLOPT_WRITEDATA, conn, err)
1248 || !CurlEasySetopt1(conn->h, CURLOPT_HEADERFUNCTION,
1249 MetaConnHeaderFunc, err)
1250 #ifdef CYGWIN
1251 || !SetCaInfo(conn, err)
1252 #endif
1253 || !CurlEasySetopt1(conn->h, CURLOPT_HEADERDATA, conn, err)) {
1254 return FALSE;
1255 }
1256
1257 mres = curl_multi_add_handle(conn->multi, conn->h);
1258 if (mres != CURLM_OK && mres != CURLM_CALL_MULTI_PERFORM) {
1259 g_set_error_literal(err, DOPE_CURLM_ERROR, mres,
1260 curl_multi_strerror(mres));
1261 return FALSE;
1262 }
1263 conn->data = g_malloc(1);
1264 conn->data_size = 0;
1265 conn->headers = g_ptr_array_new_with_free_func(g_free);
1266 conn->running = TRUE;
1267 if (conn->timer_cb) {
1268 /* If we set a callback, we must not do _perform, but wait for the cb */
1269 return TRUE;
1270 } else {
1271 return CurlConnectionPerform(conn, &still_running, err);
1272 }
1273 } else {
1274 g_set_error_literal(err, DOPE_CURLM_ERROR, 0, _("Could not init curl"));
1275 return FALSE;
1276 }
1277 return TRUE;
1278 }
1279
1280 char *CurlNextLine(CurlConnection *conn, char *ch)
1281 {
1282 char *sep_pt;
1283 if (!ch) return NULL;
1284 sep_pt = strchr(ch, conn->Terminator);
1285 if (sep_pt) {
1286 *sep_pt = '\0';
1287 if (sep_pt > ch && sep_pt[-1] == conn->StripChar) {
1288 sep_pt[-1] = '\0';
1289 }
1290 sep_pt++;
1291 }
1292 return sep_pt;
1293 }
1294
1295 /* Information associated with a specific socket */
1296 typedef struct _SockData {
1297 GIOChannel *ch;
1298 guint ev;
1299 } SockData;
1300
1301 static int timer_function(CURLM *multi, long timeout_ms, void *userp)
1302 {
1303 CurlConnection *g = userp;
1304
1305 if (g->timer_event) {
1306 dp_g_source_remove(g->timer_event);
1307 g->timer_event = 0;
1308 }
1309
1310 /* -1 means we should just delete our timer. */
1311 if (timeout_ms >= 0) {
1312 g->timer_event = dp_g_timeout_add(timeout_ms, g->timer_cb, g);
1313 }
1314 return 0;
1315 }
1316
1317 /* Clean up the SockData structure */
1318 static void remsock(SockData *f)
1319 {
1320 if (!f) {
1321 return;
1322 }
1323 if (f->ev) {
1324 dp_g_source_remove(f->ev);
1325 }
1326 g_io_channel_unref(f->ch);
1327 g_free(f);
1328 }
1329
1330 /* Assign information to a SockData structure */
1331 static void setsock(SockData *f, curl_socket_t s, CURL *e, int act,
1332 CurlConnection *g)
1333 {
1334 GIOCondition kind =
1335 ((act & CURL_POLL_IN) ? G_IO_IN : 0) |
1336 ((act & CURL_POLL_OUT) ? G_IO_OUT : 0);
1337
1338 if (f->ev) {
1339 dp_g_source_remove(f->ev);
1340 }
1341 f->ev = dp_g_io_add_watch(f->ch, kind, g->socket_cb, g);
1342 }
1343
1344 /* Initialize a new SockData structure */
1345 static void addsock(curl_socket_t s, CURL *easy, int action, CurlConnection *g)
1346 {
1347 SockData *fdp = g_malloc0(sizeof(SockData));
1348
1349 #ifdef CYGIN
1350 fdp->ch = g_io_channel_win32_new_socket(s);
1351 #else
1352 fdp->ch = g_io_channel_unix_new(s);
1353 #endif
1354 setsock(fdp, s, easy, action, g);
1355 curl_multi_assign(g->multi, s, fdp);
1356 }
1357
1358 static int socket_function(CURL *easy, curl_socket_t s, int what, void *userp,
1359 void *socketp)
1360 {
1361 CurlConnection *g = userp;
1362 SockData *fdp = socketp;
1363 if (what == CURL_POLL_REMOVE) {
1364 remsock(fdp);
1365 } else if (!fdp) {
1366 addsock(s, easy, what, g);
1367 } else {
1368 setsock(fdp, s, easy, what, g);
1369 }
1370 return 0;
1371 }
1372
1373 void SetCurlCallback(CurlConnection *conn, GSourceFunc timer_cb,
1374 GIOFunc socket_cb)
1375 {
1376 conn->timer_event = 0;
1377 conn->timer_cb = timer_cb;
1378 conn->socket_cb = socket_cb;
1379
1380 curl_multi_setopt(conn->multi, CURLMOPT_TIMERFUNCTION, timer_function);
1381 curl_multi_setopt(conn->multi, CURLMOPT_TIMERDATA, conn);
1382 curl_multi_setopt(conn->multi, CURLMOPT_SOCKETFUNCTION, socket_function);
1383 curl_multi_setopt(conn->multi, CURLMOPT_SOCKETDATA, conn);
1384 }
1385
1386 int CreateTCPSocket(LastError **error)
1387 {
1388 int fd;
1389
1390 fd = socket(AF_INET, SOCK_STREAM, 0);
1391
1392 if (fd == SOCKET_ERROR && error) {
1393 SetNetworkError(error);
1394 }
1395
1396 return fd;
1397 }
1398
1399 gboolean BindTCPSocket(int sock, const gchar *addr, unsigned port,
1400 LastError **error)
1401 {
1402 struct sockaddr_in bindaddr;
1403 int retval;
1404 struct hostent *he;
1405
1406 bindaddr.sin_family = AF_INET;
1407 bindaddr.sin_port = htons(port);
1408 if (addr && addr[0]) {
1409 he = LookupHostname(addr, error);
1410 if (!he) {
1411 return FALSE;
1412 }
1413 bindaddr.sin_addr = *((struct in_addr *)he->h_addr);
1414 } else {
1415 bindaddr.sin_addr.s_addr = INADDR_ANY;
1416 }
1417 memset(bindaddr.sin_zero, 0, sizeof(bindaddr.sin_zero));
1418
1419 retval =
1420 bind(sock, (struct sockaddr *)&bindaddr, sizeof(struct sockaddr));
1421
1422 if (retval == SOCKET_ERROR && error) {
1423 SetNetworkError(error);
1424 }
1425
1426 return (retval != SOCKET_ERROR);
1427 }
1428
1429 gboolean StartConnect(int *fd, const gchar *bindaddr, gchar *RemoteHost,
1430 unsigned RemotePort, gboolean *doneOK, LastError **error)
1431 {
1432 struct sockaddr_in ClientAddr;
1433 struct hostent *he;
1434
1435 if (doneOK)
1436 *doneOK = FALSE;
1437 he = LookupHostname(RemoteHost, error);
1438 if (!he)
1439 return FALSE;
1440
1441 *fd = CreateTCPSocket(error);
1442 if (*fd == SOCKET_ERROR)
1443 return FALSE;
1444
1445 if (bindaddr && bindaddr[0] && !BindTCPSocket(*fd, bindaddr, 0, error)) {
1446 return FALSE;
1447 }
1448
1449 ClientAddr.sin_family = AF_INET;
1450 ClientAddr.sin_port = htons(RemotePort);
1451 ClientAddr.sin_addr = *((struct in_addr *)he->h_addr);
1452 memset(ClientAddr.sin_zero, 0, sizeof(ClientAddr.sin_zero));
1453
1454 SetBlocking(*fd, FALSE);
1455
1456 if (connect(*fd, (struct sockaddr *)&ClientAddr,
1457 sizeof(struct sockaddr)) == SOCKET_ERROR) {
1458 #ifdef CYGWIN
1459 int errcode = WSAGetLastError();
1460
1461 if (errcode == WSAEWOULDBLOCK)
1462 return TRUE;
1463 else if (error)
1464 SetError(error, ET_WINSOCK, errcode, NULL);
1465 #else
1466 if (errno == EINPROGRESS)
1467 return TRUE;
1468 else if (error)
1469 SetError(error, ET_ERRNO, errno, NULL);
1470 #endif
1471 CloseSocket(*fd);
1472 *fd = -1;
1473 return FALSE;
1474 } else {
1475 if (doneOK)
1476 *doneOK = TRUE;
1477 }
1478 return TRUE;
1479 }
1480
1481 gboolean FinishConnect(int fd, LastError **error)
1482 {
1483 int errcode;
1484
1485 #ifdef CYGWIN
1486 errcode = WSAGetLastError();
1487 if (errcode == 0)
1488 return TRUE;
1489 else {
1490 if (error) {
1491 SetError(error, ET_WINSOCK, errcode, NULL);
1492 }
1493 return FALSE;
1494 }
1495 #else
1496 #ifdef HAVE_SOCKLEN_T
1497 socklen_t optlen;
1498 #else
1499 int optlen;
1500 #endif
1501
1502 optlen = sizeof(errcode);
1503 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &optlen) == -1) {
1504 errcode = errno;
1505 }
1506 if (errcode == 0)
1507 return TRUE;
1508 else {
1509 if (error) {
1510 SetError(error, ET_ERRNO, errcode, NULL);
1511 }
1512 return FALSE;
1513 }
1514 #endif /* CYGWIN */
1515 }
1516
1517 static void AddB64char(GString *str, int c)
1518 {
1519 if (c < 0)
1520 return;
1521 else if (c < 26)
1522 g_string_append_c(str, c + 'A');
1523 else if (c < 52)
1524 g_string_append_c(str, c - 26 + 'a');
1525 else if (c < 62)
1526 g_string_append_c(str, c - 52 + '0');
1527 else if (c == 62)
1528 g_string_append_c(str, '+');
1529 else
1530 g_string_append_c(str, '/');
1531 }
1532
1533 /*
1534 * Adds the plain text string "unenc" to the end of the GString "str",
1535 * using the Base64 encoding scheme.
1536 */
1537 void AddB64Enc(GString *str, gchar *unenc)
1538 {
1539 guint i;
1540 long value = 0;
1541
1542 if (!unenc || !str)
1543 return;
1544 for (i = 0; i < strlen(unenc); i++) {
1545 value <<= 8;
1546 value |= (unsigned char)unenc[i];
1547 if (i % 3 == 2) {
1548 AddB64char(str, (value >> 18) & 0x3F);
1549 AddB64char(str, (value >> 12) & 0x3F);
1550 AddB64char(str, (value >> 6) & 0x3F);
1551 AddB64char(str, value & 0x3F);
1552 value = 0;
1553 }
1554 }
1555 if (i % 3 == 1) {
1556 AddB64char(str, (value >> 2) & 0x3F);
1557 AddB64char(str, (value << 4) & 0x3F);
1558 g_string_append(str, "==");
1559 } else if (i % 3 == 2) {
1560 AddB64char(str, (value >> 10) & 0x3F);
1561 AddB64char(str, (value >> 4) & 0x3F);
1562 AddB64char(str, (value << 2) & 0x3F);
1563 g_string_append_c(str, '=');
1564 }
1565 }
1566
1567 #endif /* NETWORKING */ |