2 Syren -- a lightweight downloader for Linux/BSD/Win/MacOSX
3 inspired by Axel Copyright 2001-2002 Wilmer van der Gaast
4 version 0.0.6 (atomic alien)
5 coded by Ketmar // Avalon Group
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
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.
17 You should have received a copy of the GNU General Public License with
18 the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
19 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
20 Suite 330, Boston, MA 02111-1307 USA
28 #include "syren_tcp.h"
32 #define SY_SOCK_MSG_NO_SIGNAL 0
35 #define SY_SOCK_MSG_NO_SIGNAL SO_NOSIGPIPE
37 #define SY_SOCK_MSG_NO_SIGNAL MSG_NOSIGNAL
47 #ifdef SYOPT_ALLOW_HTTPS
48 #include "xyssl/ssl.h"
49 #include "xyssl/havege.h"
59 static int SyTCP_SSLSend (void *sock
, unsigned char *buf
, int len
) {
60 return send(((TSySocket
*)sock
)->fd
, (void *)buf
, len
, SY_SOCK_MSG_NO_SIGNAL
);
63 static int SyTCP_SSLRecv (void *sock
, unsigned char *buf
, int len
) {
64 return recv(((TSySocket
*)sock
)->fd
, (void *)buf
, len
, 0);
69 static int SyTCPLastError (void) {
71 return WSAGetLastError();
78 /* returns allocated string or NULL */
79 char *SyTCPGetLastErrorMsg (TSySocket
*fd
) {
81 if (!fd
|| !fd
->errCode
) return NULL
;
82 return SySPrintf("socket error: %i", fd
->errCode
);
84 char mbuf
[1024], *estr
;
93 #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
97 #endif /* _GNU_SOURCE */
99 if (!fd
|| !fd
->errCode
) return NULL
;
100 memset(mbuf
, 0, sizeof(mbuf
));
101 estr
= strerror_r(fd
->errCode
, mbuf
, sizeof(mbuf
));
104 return SyStrNew(estr
, -1);
107 if (!estr
) return SySPrintf("(%i) %s", fd
->errCode
, mbuf
);
109 #endif /* XSI test */
118 TSyResult
SyGetIFIP (char *ip
, const char *iface
) {
120 *ip
= '\0'; return SY_ERROR
;
125 if (!ip
) return SY_ERROR
;
127 if (!iface
|| !(*iface
)) return SY_ERROR
;
129 if (inet_aton(iface
, &inp
)) {
130 strcpy(ip
, inet_ntoa(inp
));
134 if (strlen(iface
) > IFNAMSIZ
) return SY_ERROR
;
135 int fd
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_IP
);
136 memset(&ifr
, 0, sizeof(struct ifreq
));
137 strcpy(ifr
.ifr_name
, iface
);
138 ifr
.ifr_addr
.sa_family
= AF_INET
;
139 res
= ioctl(fd
, SIOCGIFADDR
, &ifr
);
142 struct sockaddr_in
*x
= (struct sockaddr_in
*)&ifr
.ifr_addr
;
143 strcpy(ip
, inet_ntoa(x
->sin_addr
));
151 TSyResult
SyTCPInitSocket (TSySocket
*fd
) {
152 #ifdef SYOPT_ALLOW_HTTPS
153 memset(fd
, 0, sizeof(TSySocket
));
162 #ifdef SYOPT_ALLOW_HTTPS
163 TSyResult
SyTCPInitSSL (TSySocket
*fd
, int timeout
) {
167 if (fd
->fd
< 0) return SY_ERROR
;
168 if (fd
->usessl
) return SY_ERROR
;
170 fd
->sslinfo
= calloc(1, sizeof(TSySSLInfo
));
171 if (!fd
->sslinfo
) return SY_ERROR
;
174 havege_init(&(si
->hs
));
175 memset(&(si
->ssn
), 0, sizeof(ssl_session
));
177 if (ssl_init(&(si
->ssl
))) return SY_ERROR
;
178 /*ssl_set_debuglvl(&(si->ssl), 0);*/
179 ssl_set_endpoint(&(si
->ssl
), SSL_IS_CLIENT
);
180 ssl_set_authmode(&(si
->ssl
), SSL_VERIFY_NONE
); /*!!!*/
181 ssl_set_rng(&(si
->ssl
), havege_rand
, &(si
->hs
));
182 ssl_set_bio(&(si
->ssl
), SyTCP_SSLRecv
, (void *)fd
, SyTCP_SSLSend
, (void *)fd
);
183 ssl_set_ciphers(&(si
->ssl
), ssl_default_ciphers
);
185 ssl_set_session(&(si
->ssl
), 1, timeout
, &(si
->ssn
));
193 TSyResult
SyTCPCloseSocket (TSySocket
*fd
) {
194 #ifdef SYOPT_ALLOW_HTTPS
199 #ifdef SYOPT_ALLOW_HTTPS
200 if (fd
->usessl
&& fd
->sslinfo
) {
202 if (fd
->usessl
> 1) ssl_close_notify(&(si
->ssl
));
203 ssl_free(&(si
->ssl
));
204 memset(&(si
->ssl
), 0, sizeof(si
->ssl
));
207 if (fd
->sslinfo
) free(fd
->sslinfo
);
209 SySocketClose(fd
->fd
);
216 TSyResult
SyTCPConnect (TSySocket
*fd
, char *hostname
, int port
, const char *iface
, int timeout
, const TSyPrintStr
*pfn
) {
217 struct hostent
*host
= NULL
;
218 struct sockaddr_in addr
;
219 struct sockaddr_in local
;
228 SyMessage(pfn
, SY_MSG_NOTICE
, "connecting to %s:%i", hostname
, port
);
231 if (!hostname
|| !(*hostname
)) return SY_ERROR
;
232 if (iface
&& *iface
&& strcmp(iface
, "-")) {
233 SyMessage(pfn
, SY_MSG_NOTICE
, " resolving local intefrace %s...\n", iface
);
234 if (SyGetIFIP(ifip
, iface
) != SY_OK
) {
235 SyMessage(pfn
, SY_MSG_ERROR
, "can't determine interface IP for %s", iface
);
240 SyMessage(pfn
, SY_MSG_NOTICE
, " resolving %s", hostname
);
241 host
= gethostbyname(hostname
);
242 if (!host
|| !host
->h_name
|| !*host
->h_name
) {
243 fd
->errCode
= SyTCPLastError();
244 SyMessage(pfn
, SY_MSG_ERROR
, "can't resolve %s", hostname
);
248 fd
->fd
= socket(AF_INET
, SOCK_STREAM
, 0);
250 fd
->errCode
= SyTCPLastError();
251 SyMessage(pfn
, SY_MSG_ERROR
, "can't create socket");
255 SyMessage(pfn
, SY_MSG_NOTICE
, " binding to local intefrace [%s]...\n", ifip
);
256 local
.sin_family
= AF_INET
;
258 local
.sin_addr
.s_addr
= inet_addr(ifip
);
259 if (bind(fd
->fd
, (struct sockaddr
*)&local
, sizeof(struct sockaddr_in
)) == -1) {
260 fd
->errCode
= SyTCPLastError();
261 SyTCPCloseSocket(fd
);
262 SyMessage(pfn
, SY_MSG_ERROR
, "can't bind to local interface %s", ifip
);
267 addr
.sin_family
= AF_INET
;
268 addr
.sin_port
= htons(port
);
269 addr
.sin_addr
= *((struct in_addr
*)host
->h_addr
);
270 if (*ifip
) SyMessage(pfn
, SY_MSG_NOTICE
, " connecting to %s:%i (%s)", inet_ntoa(addr
.sin_addr
), port
, ifip
);
271 else SyMessage(pfn
, SY_MSG_NOTICE
, " connecting to %s:%i", inet_ntoa(addr
.sin_addr
), port
);
276 if (setsockopt(fd
->fd
, SOL_SOCKET
, SO_RCVTIMEO
, (void *)(&tmp
), 4)) {
277 SyMessage(pfn
, SY_MSG_WARNING
, " can't set receive timeout");
280 if (setsockopt(fd
->fd
, SOL_SOCKET
, SO_SNDTIMEO
, (void *)(&tmp
), 4)) {
281 SyMessage(pfn
, SY_MSG_WARNING
, " can't set send timeout");
283 tmp
= 1; setsockopt(fd
->fd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)(&tmp
), 4);
285 tv
.tv_sec
= timeout
; tv
.tv_usec
= 0; size
= sizeof(tv
);
286 if (setsockopt(fd
->fd
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, size
)) {
287 SyMessage(pfn
, SY_MSG_WARNING
, " can't set receive timeout");
289 tv
.tv_sec
= timeout
; tv
.tv_usec
= 0; size
= sizeof(tv
);
290 if (setsockopt(fd
->fd
, SOL_SOCKET
, SO_SNDTIMEO
, &tv
, size
)) {
291 SyMessage(pfn
, SY_MSG_WARNING
, " can't set send timeout");
293 val
= 1; size
= sizeof(val
);
294 setsockopt(fd
->fd
, SOL_SOCKET
, SO_KEEPALIVE
, &val
, size
);
298 if (connect(fd
->fd
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr_in
)) < 0) {
299 fd
->errCode
= SyTCPLastError();
300 SyTCPCloseSocket(fd
);
301 SyMessage(pfn
, SY_MSG_ERROR
, "can't connect to %s:%i", inet_ntoa(addr
.sin_addr
), port
);
304 SyMessage(pfn
, SY_MSG_NOTICE
, " connected to %s:%i", inet_ntoa(addr
.sin_addr
), port
);
310 TSyResult
SyTCPSend (TSySocket
*fd
, const void *buf
, int bufSize
) {
311 const char *c
= (char *)buf
;
314 if (!buf
) return SY_OK
;
315 while (bufSize
> 0) {
316 #ifdef SYOPT_ALLOW_HTTPS
319 tosend
= bufSize
>SSL_MAX_CONTENT_LEN
?SSL_MAX_CONTENT_LEN
:bufSize
;
320 wr
= ssl_write(&(((TSySSLInfo
*)fd
->sslinfo
)->ssl
), (void *)buf
, tosend
);
321 if (wr
== XYSSL_ERR_NET_TRY_AGAIN
) continue;
322 if (wr
>= 0) fd
->usessl
= 2;
323 } else wr
= send(fd
->fd
, c
, bufSize
, SY_SOCK_MSG_NO_SIGNAL
);
325 int wr
= send(fd
->fd
, c
, bufSize
, SY_SOCK_MSG_NO_SIGNAL
);
328 fd
->errCode
= SyTCPLastError();
331 c
+= wr
; bufSize
-= wr
;
337 TSyResult
SyTCPSendStr (TSySocket
*fd
, const char *str
) {
338 if (fd
< 0) return SY_ERROR
;
339 if (!str
|| !(*str
)) return SY_OK
;
340 return SyTCPSend(fd
, str
, strlen(str
));
344 /* <0: received (-res) bytes; read error */
345 int SyTCPReceiveEx (TSySocket
*fd
, void *buf
, int bufSize
, int allowPartial
) {
346 char *c
= (char *)buf
;
350 if (fd
->fd
< 0) return 0;
351 while (bufSize
> 0) {
352 #ifdef SYOPT_ALLOW_HTTPS
355 toread
= bufSize
>SSL_MAX_CONTENT_LEN
?SSL_MAX_CONTENT_LEN
:bufSize
;
356 rd
= ssl_read(&(((TSySSLInfo
*)fd
->sslinfo
)->ssl
), buf
, toread
);
357 if (rd
== XYSSL_ERR_NET_TRY_AGAIN
) continue;
358 if (rd
>= 0) fd
->usessl
= 2;
359 } else rd
= recv(fd
->fd
, c
, bufSize
, 0);
361 int rd
= recv(fd
->fd
, c
, bufSize
, 0);
363 if (rd
<= 0) fd
->errCode
= SyTCPLastError();
364 if (!rd
) return total
;
365 if (rd
< 0) return -total
;
366 c
+= rd
; total
+= rd
; bufSize
-= rd
;
367 if (allowPartial
) break;
373 int SyTCPReceive (TSySocket
*fd
, void *buf
, int bufSize
) {
374 return SyTCPReceiveEx(fd
, buf
, bufSize
, SY_TCP_DONT_ALLOWPARTIAL
);
378 TSyResult
SyTCPSendLine (const TSyPrintStr
*pfn
, int printLine
, TSySocket
*fd
, const char *fmt
, ...) {
384 if ((p
= calloc(1, size
+4)) == NULL
) { SyMessage(pfn
, SY_MSG_ERROR
, "memory error"); return SY_ERROR
; }
387 n
= vsnprintf(p
, size
, fmt
, ap
);
389 if (n
> -1 && n
< size
) break;
390 if (n
> -1) size
= n
+1; else size
*= 2;
391 if ((np
= realloc(p
, size
+4)) == NULL
) {
393 SyMessage(pfn
, SY_MSG_ERROR
, "memory error");
398 if (printLine
) SyMessage(pfn
, SY_MSG_NOTICE
, " %s", p
);
400 res
= SyTCPSendStr(fd
, p
);
402 if (res
!= SY_OK
) { SyMessage(pfn
, SY_MSG_ERROR
, "socket write error"); return SY_ERROR
; }
407 char *SyTCPReceiveStrEx (TSySocket
*fd
, int maxSize
, char *dest
) {
412 if (maxSize
<= 0 || fd
->fd
< 0 || !dest
) return NULL
;
413 memset(dest
, 0, maxSize
+1);
417 #ifdef SYOPT_ALLOW_HTTPS
421 rd
= recv(fd
->fd
, d
, maxSize
+2, MSG_PEEK
);
425 #endif /* SYOPT_ALLOW_HTTPS */
428 #ifdef SYOPT_ALLOW_HTTPS
430 rd
= ssl_read(&(((TSySSLInfo
*)fd
->sslinfo
)->ssl
), (void *)d
, 1);
431 if (rd
== XYSSL_ERR_NET_TRY_AGAIN
) continue;
432 if (rd
>= 0) fd
->usessl
= 2;
435 rd
= recv(fd
->fd
, d
, 1, 0);
437 if (rd
<= 0) fd
->errCode
= SyTCPLastError();
438 if (rd
< 0) return NULL
;
439 if (rd
== 0) break; /* connection closed */
440 /* check for end of headers */
444 if (!ch
) return NULL
; /* can't got zero byte here */
446 /* yes! all chars are here */
448 /* receive peeked bytes */
449 rd
= recv(fd
->fd
, d
, f
, 0);
450 if (rd
< 0) return NULL
;
452 if (chp
> dest
&& *(chp
-1) == '\r') chp
--; /* skip CR */
459 /* receive peeked bytes */
460 rd
= recv(fd
->fd
, d
, rd
, 0);
461 if (rd
<= 0) return NULL
;
463 maxSize
-= rd
; d
+= rd
;
465 /* headers too big */
470 char *SyTCPReceiveStr (TSySocket
*fd
, int maxSize
) {
474 if (maxSize
<= 0 || fd
->fd
< 0) return NULL
;
475 dest
= calloc(1, maxSize
+8);
476 if (!dest
) return NULL
;
477 res
= SyTCPReceiveStrEx(fd
, maxSize
, dest
);
478 if (!res
) { free(dest
); return NULL
; }
483 /* return NULL if string was too big or on error;
484 tries to receive all headers
486 char *SyTCPReceiveHdrs (TSySocket
*fd
, int maxSize
) {
487 char ch
, *d
, *chp
, *dest
;
491 if (maxSize
<= 0 || fd
->fd
< 0) return NULL
;
492 dest
= calloc(1, maxSize
+8);
497 int rd = recv(fd->fd, d, maxSize+2, MSG_PEEK);
501 if (!rd) { rcv = 1; rd = recv(fd->fd, d, 1, 0); } else rcv = 0;
504 #ifdef SYOPT_ALLOW_HTTPS
508 rd
= recv(fd
->fd
, d
, maxSize
+2, MSG_PEEK
);
512 #endif /* SYOPT_ALLOW_HTTPS */
515 #ifdef SYOPT_ALLOW_HTTPS
517 rd
= ssl_read(&(((TSySSLInfo
*)fd
->sslinfo
)->ssl
), (void *)d
, 1);
518 if (rd
== XYSSL_ERR_NET_TRY_AGAIN
) continue;
519 if (rd
>= 0) fd
->usessl
= 2;
522 rd
= recv(fd
->fd
, d
, 1, 0);
524 if (rd
<= 0) fd
->errCode
= SyTCPLastError();
529 if (rd
== 0) break; /* connection closed */
530 /* check for end of headers */
533 ch
= *chp
; chp
++; f
++;
534 if (!ch
) { free(dest
); return NULL
; } /* can't got zero byte here */
536 /* check for empty line */
537 l
= (chp
-dest
)-2; /* prev ch index */
538 if (l
>= 0 && dest
[l
] == '\r') l
--; /* skip CR */
539 if (l
>= 0 && dest
[l
] == '\n') {
540 /* yes! all headers are here */
542 /* receive peeked bytes */
543 rd
= recv(fd
->fd
, d
, f
, 0);
544 if (rd
< 0) { free(dest
); return NULL
; }
552 /* receive peeked bytes */
553 rd
= recv(fd
->fd
, d
, rd
, 0);
554 if (rd
<= 0) { free(dest
); return NULL
; }
556 maxSize
-= rd
; d
+= rd
;
558 /* headers too big */