/* returns 0 if it could create lock; 1 if the lock exists; -1 on error */ int xiogetlock(const char *lockfile) { char *s; struct stat strat; int fd; pid_t pid; char pidbuf[3*sizeof(pid_t)+1]; size_t bytes; if (Lstat(lockfile, &strat) == 0) { return 1; } switch (errno) { case ENOENT: break; default: Error3("Lstat(\"%s\", %p): %s", lockfile, &strat, strerror(errno)); return -1; } /* in this moment, the file did not exist */ if ((s = Malloc(strlen(lockfile)+8)) == NULL) { errno = ENOMEM; return -1; } strcpy(s, lockfile); strcat(s, ".XXXXXX"); if ((fd = Mkstemp(s)) < 0) { Error2("mkstemp(\"%s\"): %s", s, strerror(errno)); return -1; } pid = Getpid(); bytes = sprintf(pidbuf, F_pid, pid); if (writefull(fd, pidbuf, bytes) < 0) { Error4("write(%d, %p, "F_Zu"): %s", fd, pidbuf, bytes, strerror(errno)); return -1; } Close(fd); /* Chmod(lockfile, 0600); */ if (Link(s, lockfile) < 0) { int _errno = errno; Error3("link(\"%s\", \"%s\"): %s", s, lockfile, strerror(errno)); Unlink(s); errno = _errno; return -1; } Unlink(s); return 0; }
/* perform socks4 client dialog on existing FD. Called within fork/retry loop, after connect() */ int _xioopen_socks4_connect(struct single *xfd, struct socks4 *sockhead, size_t headlen, int level) { ssize_t bytes; int result; unsigned char buff[SIZEOF_STRUCT_SOCKS4]; struct socks4 *replyhead = (struct socks4 *)buff; char *destdomname = NULL; /* send socks header (target addr+port, +auth) */ #if WITH_MSGLEVEL <= E_INFO if (ntohl(sockhead->dest) <= 0x000000ff) { destdomname = strchr(sockhead->userid, '\0')+1; } Info11("sending socks4%s request VN=%d DC=%d DSTPORT=%d DSTIP=%d.%d.%d.%d USERID=%s%s%s", destdomname?"a":"", sockhead->version, sockhead->action, ntohs(sockhead->port), ((unsigned char *)&sockhead->dest)[0], ((unsigned char *)&sockhead->dest)[1], ((unsigned char *)&sockhead->dest)[2], ((unsigned char *)&sockhead->dest)[3], sockhead->userid, destdomname?" DESTNAME=":"", destdomname?destdomname:""); #endif /* WITH_MSGLEVEL <= E_INFO */ #if WITH_MSGLEVEL <= E_DEBUG { char *msgbuff; if ((msgbuff = Malloc(3*headlen)) != NULL) { xiohexdump((const unsigned char *)sockhead, headlen, msgbuff); Debug1("sending socks4(a) request data %s", msgbuff); } } #endif /* WITH_MSGLEVEL <= E_DEBUG */ if (writefull(xfd->fd, sockhead, headlen) < 0) { Msg4(level, "write(%d, %p, "F_Zu"): %s", xfd->fd, sockhead, headlen, strerror(errno)); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } return STAT_RETRYLATER; /* retry complete open cycle */ } bytes = 0; Info("waiting for socks reply"); while (bytes >= 0) { /* loop over answer chunks until complete or error */ /* receive socks answer */ do { result = Read(xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes); } while (result < 0 && errno == EINTR); if (result < 0) { Msg4(level, "read(%d, %p, "F_Zu"): %s", xfd->fd, buff+bytes, SIZEOF_STRUCT_SOCKS4-bytes, strerror(errno)); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } } if (result == 0) { Msg(level, "read(): EOF during read of socks reply, peer might not be a socks4 server"); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } return STAT_RETRYLATER; } #if WITH_MSGLEVEL <= E_DEBUG { char msgbuff[3*SIZEOF_STRUCT_SOCKS4]; * xiohexdump((const unsigned char *)replyhead+bytes, result, msgbuff) = '\0'; Debug2("received socks4 reply data (offset "F_Zd"): %s", bytes, msgbuff); } #endif /* WITH_MSGLEVEL <= E_DEBUG */ bytes += result; if (bytes == SIZEOF_STRUCT_SOCKS4) { Debug1("received all "F_Zd" bytes", bytes); break; } Debug2("received %d bytes, waiting for "F_Zu" more bytes", result, SIZEOF_STRUCT_SOCKS4-bytes); } if (result <= 0) { /* we had a problem while reading socks answer */ return STAT_RETRYLATER; /* retry complete open cycle */ } Info7("received socks reply VN=%u CD=%u DSTPORT=%u DSTIP=%u.%u.%u.%u", replyhead->version, replyhead->action, ntohs(replyhead->port), ((uint8_t *)&replyhead->dest)[0], ((uint8_t *)&replyhead->dest)[1], ((uint8_t *)&replyhead->dest)[2], ((uint8_t *)&replyhead->dest)[3]); if (replyhead->version != 0) { Warn1("socks: reply code version is not 0 (%d)", replyhead->version); } switch (replyhead->action) { case SOCKS_CD_GRANTED: /* Notice("socks: connect request succeeded"); */ #if 0 if (Getsockname(xfd->fd, (struct sockaddr *)&us, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", xfd->fd, &us, uslen, strerror(errno)); } Notice1("successfully connected from %s via socks4", sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff))); #else Notice("successfully connected via socks4"); #endif break; case SOCKS_CD_FAILED: Msg(level, "socks: connect request rejected or failed"); return STAT_RETRYLATER; case SOCKS_CD_NOIDENT: Msg(level, "socks: ident refused by client"); return STAT_RETRYLATER; case SOCKS_CD_IDENTFAILED: Msg(level, "socks: ident failed"); return STAT_RETRYLATER; default: Msg1(level, "socks: undefined status %u", replyhead->action); } return STAT_OK; }
int _xioopen_proxy_connect(struct single *xfd, struct proxyvars *proxyvars, int level) { size_t offset; char request[CONNLEN]; char buff[BUFLEN+1]; #if CONNLEN > BUFLEN #error not enough buffer space #endif char textbuff[2*BUFLEN+1]; /* just for sanitizing print data */ char *eol = buff; int state; ssize_t sresult; /* generate proxy request header - points to final target */ sprintf(request, "CONNECT %s:%u HTTP/1.0\r\n", proxyvars->targetaddr, proxyvars->targetport); /* send proxy CONNECT request (target addr+port) */ * xiosanitize(request, strlen(request), textbuff) = '\0'; Info1("sending \"%s\"", textbuff); /* write errors are assumed to always be hard errors, no retry */ if (writefull(xfd->fd, request, strlen(request)) < 0) { Msg4(level, "write(%d, %p, "F_Zu"): %s", xfd->fd, request, strlen(request), strerror(errno)); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } return STAT_RETRYLATER; } if (proxyvars->authstring) { /* send proxy authentication header */ # define XIOAUTHHEAD "Proxy-authorization: Basic " # define XIOAUTHLEN 27 static const char *authhead = XIOAUTHHEAD; # define HEADLEN 256 char *header, *next; /* ...\r\n\0 */ if ((header = Malloc(XIOAUTHLEN+((strlen(proxyvars->authstring)+2)/3)*4+3)) == NULL) { return -1; } strcpy(header, authhead); next = xiob64encodeline(proxyvars->authstring, strlen(proxyvars->authstring), strchr(header, '\0')); *next = '\0'; Info1("sending \"%s\\r\\n\"", header); *next++ = '\r'; *next++ = '\n'; *next++ = '\0'; if (writefull(xfd->fd, header, strlen(header)) < 0) { Msg4(level, "write(%d, %p, "F_Zu"): %s", xfd->fd, header, strlen(header), strerror(errno)); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } return STAT_RETRYLATER; } free(header); } Info("sending \"\\r\\n\""); if (writefull(xfd->fd, "\r\n", 2) < 0) { Msg2(level, "write(%d, \"\\r\\n\", 2): %s", xfd->fd, strerror(errno)); if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } return STAT_RETRYLATER; } /* request is kept for later error messages */ *strstr(request, " HTTP") = '\0'; /* receive proxy answer; looks like "HTTP/1.0 200 .*\r\nHeaders..\r\n\r\n" */ /* socat version 1 depends on a valid fd for data transfer; address therefore cannot buffer data. So, to prevent reading beyond the end of the answer headers, only single bytes are read. puh. */ state = XIOSTATE_HTTP1; offset = 0; /* up to where the buffer is filled (relative) */ /*eol;*/ /* points to the first lineterm of the current line */ do { sresult = xioproxy_recvbytes(xfd, buff+offset, 1, level); if (sresult <= 0) { state = XIOSTATE_ERROR; break; /* leave read cycles */ } switch (state) { case XIOSTATE_HTTP1: /* 0 or more bytes of first line received, no '\r' yet */ if (*(buff+offset) == '\r') { eol = buff+offset; state = XIOSTATE_HTTP2; break; } if (proxyvars->ignorecr && *(buff+offset) == '\n') { eol = buff+offset; state = XIOSTATE_HTTP3; break; } break; case XIOSTATE_HTTP2: /* first line received including '\r' */ if (*(buff+offset) != '\n') { state = XIOSTATE_HTTP1; break; } state = XIOSTATE_HTTP3; break; case XIOSTATE_HTTP3: /* received status (first line) and "\r\n" */ if (*(buff+offset) == '\r') { state = XIOSTATE_HTTP7; break; } if (proxyvars->ignorecr && *(buff+offset) == '\n') { state = XIOSTATE_HTTP8; break; } state = XIOSTATE_HTTP4; break; case XIOSTATE_HTTP4: /* within header */ if (*(buff+offset) == '\r') { eol = buff+offset; state = XIOSTATE_HTTP5; break; } if (proxyvars->ignorecr && *(buff+offset) == '\n') { eol = buff+offset; state = XIOSTATE_HTTP6; break; } break; case XIOSTATE_HTTP5: /* within header, '\r' received */ if (*(buff+offset) != '\n') { state = XIOSTATE_HTTP4; break; } state = XIOSTATE_HTTP6; break; case XIOSTATE_HTTP6: /* received status (first line) and 1 or more headers, "\r\n" */ if (*(buff+offset) == '\r') { state = XIOSTATE_HTTP7; break; } if (proxyvars->ignorecr && *(buff+offset) == '\n') { state = XIOSTATE_HTTP8; break; } state = XIOSTATE_HTTP4; break; case XIOSTATE_HTTP7: /* received status (first line), 0 or more headers, "\r\n\r" */ if (*(buff+offset) == '\n') { state = XIOSTATE_HTTP8; break; } if (*(buff+offset) == '\r') { if (proxyvars->ignorecr) { break; /* ignore it, keep waiting for '\n' */ } else { state = XIOSTATE_HTTP5; } break; } state = XIOSTATE_HTTP4; break; } ++offset; /* end of status line reached */ if (state == XIOSTATE_HTTP3) { char *ptr; /* set a terminating null - on or after CRLF? */ *(buff+offset) = '\0'; * xiosanitize(buff, Min(offset, (sizeof(textbuff)-1)>>1), textbuff) = '\0'; Info1("proxy_connect: received answer \"%s\"", textbuff); *eol = '\0'; * xiosanitize(buff, Min(strlen(buff), (sizeof(textbuff)-1)>>1), textbuff) = '\0'; if (strncmp(buff, "HTTP/1.0 ", 9) && strncmp(buff, "HTTP/1.1 ", 9)) { /* invalid answer */ Msg1(level, "proxy: invalid answer \"%s\"", textbuff); return STAT_RETRYLATER; } ptr = buff+9; /* skip multiple spaces */ while (*ptr == ' ') ++ptr; /* HTTP answer */ if (strncmp(ptr, "200", 3)) { /* not ok */ /* CERN: "HTTP/1.0 200 Connection established" "HTTP/1.0 400 Invalid request "CONNECT 10.244.9.3:8080 HTTP/1.0" (unknown method)" "HTTP/1.0 403 Forbidden - by rule" "HTTP/1.0 407 Proxy Authentication Required" Proxy-Authenticate: Basic realm="Squid proxy-caching web server" > 50 72 6f 78 79 2d 61 75 74 68 6f 72 69 7a 61 74 Proxy-authorizat > 69 6f 6e 3a 20 42 61 73 69 63 20 61 57 4e 6f 63 ion: Basic aWNoc > 32 56 73 59 6e 4e 30 4f 6e 4e 30 63 6d 56 75 5a 2VsYnN0OnN0cmVuZ > 32 64 6c 61 47 56 70 62 51 3d 3d 0d 0a 2dlaGVpbQ==.. b64encode("username:password") "HTTP/1.0 500 Can't connect to host" */ /* Squid: "HTTP/1.0 400 Bad Request" "HTTP/1.0 403 Forbidden" "HTTP/1.0 503 Service Unavailable" interesting header: "X-Squid-Error: ERR_CONNECT_FAIL 111" */ /* Apache: "HTTP/1.0 400 Bad Request" "HTTP/1.1 405 Method Not Allowed" */ /* WTE: "HTTP/1.1 200 Connection established" "HTTP/1.1 404 Host not found or not responding, errno: 79" "HTTP/1.1 404 Host not found or not responding, errno: 32" "HTTP/1.1 404 Host not found or not responding, errno: 13" */ /* IIS: "HTTP/1.1 404 Object Not Found" */ ptr += 3; while (*ptr == ' ') ++ptr; Msg2(level, "%s: %s", request, ptr); return STAT_RETRYLATER; } /* ok!! */ /* "HTTP/1.0 200 Connection established" */ /*Info1("proxy: \"%s\"", textbuff+13);*/ offset = 0; } else if (state == XIOSTATE_HTTP6) { /* end of a header line reached */ char *endp; /* set a terminating null */ *(buff+offset) = '\0'; endp = xiosanitize(buff, Min(offset, (sizeof(textbuff)-1)>>1), textbuff); *endp = '\0'; Info1("proxy_connect: received header \"%s\"", textbuff); offset = 0; }