int h_connect(int so, const char *host, const char *port, BINKD_CONFIG *config, const char *proxy, const char *socks) { int ntlm = 0; #ifdef NTLM char *ntlmsp = NULL; #endif int i, n; struct addrinfo *ai, *aiHead, hints; int aiErr; char buf[1024], *pbuf; char *sp, *sauth; struct in_addr defaddr; /* setup hints for getaddrinfo */ memset((void *)&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (proxy[0]) { strncpy(buf, proxy, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; if ((sp=strchr(buf, '/')) != NULL) *sp++ = '\0'; Log(4, "connected to proxy %s", buf); if(sp) { char *sp1; #ifdef NTLM if(strchr(sp, '/') != strrchr(sp, '/')) { ntlm = 1; ntlmsp = sp = strdup(sp); } else #endif { if((sp1=strchr(sp, '/'))!=NULL) sp1[0]=':'; sp1=malloc(strlen(sp)*4/3 + 4); sp1[enbase64(sp, strlen(sp), sp1)] = 0; sp = sp1; } } memset(buf, 0, sizeof(buf)); if (socks[0]) { char *sp1; strncpy(buf, socks, sizeof(buf)-6); if((sp1=strchr(buf, '/'))!=NULL) sp1[0]=0; if((sp1=strchr(buf, ':'))==NULL) strcat(buf, ":1080"); sp1=strdup(buf); i=snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.%d\r\n", sp1, ntlm); free(sp1); } else i=snprintf(buf, sizeof(buf), "CONNECT %s:%s HTTP/1.%d\r\n", host, port, ntlm); if (sp) { #ifdef NTLM if (ntlm) { if (i<sizeof(buf)) i+=snprintf(buf+i, sizeof(buf)-i, "Connection: keep-alive\r\n"); if (i<sizeof(buf)) i+=snprintf(buf+i, sizeof(buf)-i, "Proxy-Authorization: NTLM "); if (i<sizeof(buf)) getNTLM1(sp, buf+i, sizeof(buf)-i); if (sizeof(buf)>strlen(buf)+3) strcat(buf, "\r\n"); i = strlen(buf); } else #endif { if (i<sizeof(buf)) i+=snprintf(buf+i, sizeof(buf)-i, "Proxy-Authorization: basic %s\r\n", sp); free(sp); } } if (sizeof(buf) > i+2) { buf[i++]='\r'; buf[i++]='\n'; } if (send(so, buf, i, 0) < 0) { Log(4, "Send to proxy error: %s", TCPERR()); SetTCPError(PR_ERROR); return 1; } Log(10, "sent proxy sockfd %d request: %s", so, buf); for(i=0; i<sizeof(buf)-1; i++) { struct timeval tv; fd_set fds; FD_ZERO(&fds); FD_SET(so, &fds); tv.tv_sec=config->nettimeout; tv.tv_usec=0; if ((n=select(so+1, &fds, NULL, NULL, config->nettimeout > 0 ? &tv : NULL)) < 1) { if (n<0) Log(4, "proxy error: %s", TCPERR()); else Log(4, "proxy timeout..."); SetTCPError(PR_ERROR); return 1; } if ((n=recv(so, buf+i, 1, 0)) < 1) { if (n<0) Log(2, "Proxy error: %s", TCPERR()); else Log(2, "Connection closed by proxy..."); SetTCPError(PR_ERROR); return 1; } buf[i+1]=0; if((i+2>=sizeof(buf))||((sp=strstr(buf, "\r\n\r\n"))!=NULL)||((sp=strstr(buf, "\n\n"))!=NULL)) { #ifdef NTLM if ((ntlm) && ((sp=strstr(buf, "uthenticate: NTLM "))!=NULL)) { char *sp1 = strstr(buf, "Content-Length: "); sp = strdup(sp+18); if (sp1) { int j=atoi(sp1+16); for(;j>0; j--) { if(recv(so, buf+i, 1, 0)<1) break; } } memset(buf, 0, sizeof(buf)); i=snprintf(buf, sizeof(buf), "CONNECT %s:%s HTTP/1.%d\r\nProxy-Authorization: NTLM ", host, port, ntlm); i = getNTLM2(ntlmsp, sp, buf + i, sizeof(buf) - i); free(sp); if (i) Log(2, "Invalid username/password/host/domain string (%s) %d", ntlmsp, i); free(ntlmsp); if(!i) { ntlm = 0; if (strlen(buf)+3<sizeof(buf)) strcat(buf, "\r\n\r\n"); send(so, buf, strlen(buf), 0); i=0; continue; } } #endif if((sp=strchr(buf, '\n'))!=NULL) { sp[0]=0; sp--; if(sp[0]=='\r') sp[0]=0; } if(strstr(buf, " 200 ")) break; Log(2, "Connection rejected by proxy (%s)", buf); SetTCPError(PR_ERROR); return 1; } } } if (socks[0]) { strncpy(buf, socks, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; if ((sauth=strchr(buf, '/')) != NULL) *sauth++ = '\0'; Log(4, "connected to socks%c %s", sauth ? '5' : '4', buf); if (!sauth) /* SOCKS4 */ { /* SOCKS4 only support IPv4 and we need the IP address */ if ((aiErr=srv_getaddrinfo(host, port, &hints, &aiHead)) != 0) { Log(2, "getaddrinfo failed: %s (%d)", gai_strerror(aiErr), aiErr); SetTCPError(PR_ERROR); return 1; } } else /* SOCKS5 */ { if ((aiErr=getaddrinfo(NULL, port, &hints, &aiHead)) != 0) { Log(2, "getaddrinfo failed: %s (%d)", gai_strerror(aiErr), aiErr); return 1; } sauth=strdup(sauth); sp=strchr(sauth, '/'); buf[0]=5; buf[2]=0; if(!sauth[0]) { buf[1]=1; send(so, buf, 3, 0); } else { buf[1]=2; if(sp) buf[3]=2; else buf[3]=1; send(so, buf, 4, 0); } if ((recv(so, buf, 2, 0)!=2)||((buf[1])&&(buf[1]!=1)&&(buf[1]!=2))) { Log(1, "Auth. method not supported by socks5 server"); free(sauth); freeaddrinfo(aiHead); SetTCPError(PR_ERROR); return 1; } Log(6, "Socks5, Auth=%d", buf[1]); if (buf[1]==2) /* username/password method */ { buf[0]=1; if (!sp) i=strlen(sauth); else i=(sp-sauth); buf[1]=i; memcpy(buf+2, sauth, i); i+=2; if (!sp) buf[i++]=0; else { sp++; buf[i++]=strlen(sp); strcpy(buf+i, sp); i+=strlen(sp); } send(so, buf, i, 0); buf[0]=buf[1]=0; if ((recv(so, buf, 2, 0)<2)||(buf[1])) { Log(1, "Authentication failed (socks5 returns %02X%02X)", (unsigned char)buf[0], (unsigned char)buf[1]); free(sauth); freeaddrinfo(aiHead); SetTCPError(PR_ERROR); return 1; } } } for (ai = aiHead; ai != NULL; ai = ai->ai_next) { unsigned short portnum = ntohs(((struct sockaddr_in*)(ai->ai_addr))->sin_port); if (!sauth) /* SOCKS4 */ { buf[0]=4; buf[1]=1; lockhostsem(); Log (4, strcmp(port, config->oport) == 0 ? "trying %s..." : "trying %s:%u...", inet_ntoa(((struct sockaddr_in*)(ai->ai_addr))->sin_addr), portnum); releasehostsem(); buf[2]=(unsigned char)((portnum>>8)&0xFF); buf[3]=(unsigned char)(portnum&0xFF); memcpy(buf+4, &(((struct sockaddr_in*)(ai->ai_addr))->sin_addr), 4); buf[8]=0; send(so, buf, 9, 0); } else /* SOCKS5 */ { buf[0]=5; buf[1]=1; buf[2]=0; if (isdigit(host[0]) && (defaddr.s_addr = inet_addr (host)) != INADDR_NONE) { buf[3]=1; memcpy(buf+4, &defaddr, 4); pbuf = buf+8; } else { buf[3]=3; i = strlen(host); buf[4]=(unsigned char)i; memcpy(buf+5, host, i); pbuf = buf+5+i; } *pbuf++=(unsigned char)((portnum>>8)&0xFF); *pbuf++=(unsigned char)(portnum&0xFF); send(so, buf, pbuf-buf, 0); } for (i=0; i<sizeof(buf); i++) { struct timeval tv; fd_set fds; FD_ZERO(&fds); FD_SET(so, &fds); tv.tv_sec=config->nettimeout; tv.tv_usec=0; if ((n=select(so+1, &fds, NULL, NULL, config->nettimeout > 0 ? &tv : NULL)) < 1) { if (n<0) Log(4, "socks error: %s", TCPERR()); else Log(4, "socks timeout..."); if (sauth) free(sauth); freeaddrinfo(aiHead); SetTCPError(PR_ERROR); return 1; } if ((n=recv(so, buf+i, 1, 0))<1) { if (n<0) Log(2, "socks error: %s", TCPERR()); Log(2, "connection closed by socks server..."); if (sauth) free(sauth); freeaddrinfo(aiHead); SetTCPError(PR_ERROR); return 1; } if (!sauth && i>6) /* 8th byte received */ { if (buf[0]!=0) { Log(2, "Bad reply from socks server"); freeaddrinfo(aiHead); SetTCPError(PR_ERROR); return 1; } if (buf[1]!=90) { Log(2, "connection rejected by socks4 server (%d)", (unsigned char)buf[1]); SetTCPError(PR_ERROR); break; /* try next IP */ } else { freeaddrinfo(aiHead); return 0; } } else if (sauth && i>5) { if (buf[0]!=5) { Log(2, "Bad reply from socks server"); free(sauth); freeaddrinfo(aiHead); SetTCPError(PR_ERROR); return 1; } if ((buf[3]==1) && (i<9)) continue; if ((buf[3]==3) && (i<(6+(unsigned char)buf[4]))) continue; if ((buf[3]==4) && (i<21)) continue; free(sauth); freeaddrinfo(aiHead); if (!buf[1]) return 0; switch (buf[1]) { case 1: Log (2, "general SOCKS5 server failure"); break; case 2: Log (2, "connection not allowed by ruleset (socks5)"); break; case 3: Log (2, "Network unreachable (socks5)"); break; case 4: Log (2, "Host unreachable (socks5)"); break; case 5: Log (2, "Connection refused (socks5)"); break; case 6: Log (2, "TTL expired (socks5)"); break; case 7: Log (2, "Command not supported by socks5"); break; case 8: Log (2, "Address type not supported"); break; default: Log (2, "Unknown reply (0x%02X) from socks5 server", (unsigned char)buf[1]); } SetTCPError(PR_ERROR); return 1; } } }
static int call0 (FTN_NODE *node, BINKD_CONFIG *config) { int sockfd = INVALID_SOCKET; int sock_out; char szDestAddr[FTN_ADDR_SZ + 1]; int i, rc, pid = -1; char host[BINKD_FQDNLEN + 5 + 1]; /* current host/port */ char addrbuf[BINKD_FQDNLEN + 1]; char servbuf[MAXSERVNAME + 1]; char *hosts; char *port; char *dst_host = host; const char *save_err; #ifdef HTTPS int use_proxy; char *proxy, *socks; struct addrinfo *aiProxyHead; #endif struct addrinfo *ai, *aiNodeHead, *aiHead, hints; int aiErr; /* setup hints for getaddrinfo */ memset((void *)&hints, 0, sizeof(hints)); hints.ai_family = node->IP_afamily; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; #ifdef WITH_PERL hosts = xstrdup(node->hosts); #ifdef HTTPS proxy = xstrdup(config->proxy); socks = xstrdup(config->socks); #endif if (!perl_on_call(node, config, &hosts #ifdef HTTPS , &proxy, &socks #endif )) { Log(1, "call aborted by Perl on_call()"); return 0; } #else hosts = node->hosts; #ifdef HTTPS proxy = config->proxy; socks = config->socks; #endif #endif ftnaddress_to_str (szDestAddr, &node->fa); Log (2, "call to %s", szDestAddr); setproctitle ("call to %s", szDestAddr); #ifdef HTTPS use_proxy = (node->NP_flag != NP_ON) && (!node->pipe || !node->pipe[0]) && (proxy[0] || socks[0]); if (use_proxy) { char *sp, *sport; strncpy(host, proxy[0] ? proxy : socks, sizeof(host)); if ((sp=strchr(host, ':')) != NULL) { *sp++ = '\0'; sport = sp; if ((sp=strchr(sp, '/')) != NULL) *sp++ = '\0'; } else { if ((sp=strchr(host, '/')) != NULL) *sp++ = '\0'; sport = proxy[0] ? "squid" : "socks"; /* default port */ } /* resolve proxy host */ if ( (aiErr = srv_getaddrinfo(host, sport, &hints, &aiProxyHead)) != 0) { Log(2, "Port %s not found, try default %d", sp, proxy[0] ? 3128 : 1080); aiErr = getaddrinfo(host, proxy[0] ? "3128" : "1080", &hints, &aiProxyHead); } if (aiErr != 0) { Log(1, "%s host %s not found", proxy[0] ? "Proxy" : "Socks", host); #ifdef WITH_PERL xfree(hosts); #ifdef HTTPS xfree(proxy); xfree(socks); #endif #endif return 0; } } #endif for (i = 1; sockfd == INVALID_SOCKET && (rc = get_host_and_port (i, host, &port, hosts, &node->fa, config)) != -1; ++i) { if (rc == 0) { Log (1, "%s: %i: error parsing host list", hosts, i); continue; } pid = -1; if (node->pipe && node->pipe[0]) { char *cmdline = strdup(node->pipe); cmdline = ed(cmdline, "*H", host, NULL); cmdline = ed(cmdline, "*I", port, NULL); pid = run3(cmdline, &sock_out, &sockfd, NULL); free(cmdline); if (pid != -1) { Log (4, "connected"); add_socket(sock_out); break; } if (!binkd_exit) { Log (1, "connection to %s failed"); /* bad_try (&node->fa, "exec error", BAD_CALL, config); */ } sockfd = INVALID_SOCKET; continue; } #ifdef HTTPS if (use_proxy) aiHead = aiProxyHead; else /* don't resolve if proxy or socks specified */ #endif { aiErr = srv_getaddrinfo(host, port, &hints, &aiNodeHead); if (aiErr != 0) { bad_try(&node->fa, "Cannot getaddrinfo", BAD_CALL, config); continue; } aiHead = aiNodeHead; } /* Trying... */ for (ai = aiHead; ai != NULL && sockfd == INVALID_SOCKET; ai = ai->ai_next) { if ((sockfd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == INVALID_SOCKET) { Log (1, "socket: %s", TCPERR ()); /* as long as there are more addresses, try those */ if (ai != NULL) continue; else { #ifdef WITH_PERL xfree(hosts); #ifdef HTTPS xfree(proxy); xfree(socks); #endif #endif freeaddrinfo(aiHead); return 0; } } add_socket(sockfd); /* Was the socket created after close_sockets loop in exitfunc()? */ if (binkd_exit) { #ifdef WITH_PERL xfree(hosts); #ifdef HTTPS xfree(proxy); xfree(socks); #endif #endif freeaddrinfo(aiHead); return 0; } rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, addrbuf, sizeof(addrbuf), servbuf, sizeof(servbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (rc != 0) { Log (2, "Error in getnameinfo(): %s (%d)", gai_strerror(rc), rc); snprintf(addrbuf, BINKD_FQDNLEN, "invalid"); *servbuf = '\0'; } #ifdef HTTPS if (use_proxy) { char *sp = strchr(host, ':'); if (sp) *sp = '\0'; if (port == config->oport) Log (4, "trying %s via %s %s:%s...", host, proxy[0] ? "proxy" : "socks", addrbuf, servbuf); else Log (4, "trying %s:%s via %s %s:%s...", host, port, proxy[0] ? "proxy" : "socks", addrbuf, servbuf); sprintf(host+strlen(host), ":%s", port); } else #endif { if (port == config->oport) Log (4, "trying %s [%s]...", host, addrbuf); else Log (4, "trying %s [%s]:%s...", host, addrbuf, servbuf); } /* find bind addr with matching address family */ if (config->bindaddr[0]) { struct addrinfo *src_ai, src_hints; memset((void *)&src_hints, 0, sizeof(src_hints)); src_hints.ai_socktype = SOCK_STREAM; src_hints.ai_family = ai->ai_family; src_hints.ai_protocol = IPPROTO_TCP; if ((aiErr = getaddrinfo(config->bindaddr, NULL, &src_hints, &src_ai)) == 0) { if (bind(sockfd, src_ai->ai_addr, src_ai->ai_addrlen)) Log(4, "bind: %s", TCPERR()); freeaddrinfo(src_ai); } else if (aiErr == EAI_FAMILY) /* address family of target and bind address don't match */ continue; else /* otherwise just warn and don't bind() */ Log(2, "bind -- getaddrinfo: %s (%d)", gai_strerror(aiErr), aiErr); } #ifdef HAVE_FORK if (config->connect_timeout) { signal(SIGALRM, alrm); alarm(config->connect_timeout); } #endif if (connect (sockfd, ai->ai_addr, ai->ai_addrlen) == 0) { #ifdef HAVE_FORK alarm(0); #endif Log (4, "connected"); sock_out = sockfd; dst_host = addrbuf; break; } #ifdef HAVE_FORK if (errno == EINTR && config->connect_timeout) save_err = strerror (ETIMEDOUT); else save_err = TCPERR (); alarm(0); #else save_err = TCPERR (); #endif if (!binkd_exit) { Log (1, "connection to %s failed: %s", szDestAddr, save_err); bad_try (&node->fa, save_err, BAD_CALL, config); } del_socket(sockfd); soclose (sockfd); sockfd = INVALID_SOCKET; } #ifdef HTTPS if (!use_proxy) #endif freeaddrinfo(aiNodeHead); #ifdef HTTPS if (sockfd != INVALID_SOCKET && use_proxy) { if (h_connect(sockfd, host, config, proxy, socks) != 0) { if (!binkd_exit) bad_try (&node->fa, TCPERR (), BAD_CALL, config); del_socket(sockfd); soclose (sockfd); sockfd = INVALID_SOCKET; } else if (port == config->oport) { char *pp; if( (pp = strchr(host, ':')) ){ *pp = '\0'; } } } #endif } #ifdef HTTPS if (use_proxy) freeaddrinfo(aiProxyHead); #endif #ifdef WITH_PERL xfree(hosts); #ifdef HTTPS xfree(proxy); xfree(socks); #endif #endif if (sockfd == INVALID_SOCKET) return 0; protocol (sockfd, sock_out, node, NULL, dst_host, config); if (pid != -1) { del_socket(sock_out); close(sock_out); #ifdef HAVE_WAITPID if (waitpid (pid, &rc, 0) == -1) { Log (1, "waitpid(%u) error: %s", pid, strerror(errno)); } else { if (WIFSIGNALED(rc)) Log (2, "process %u exited by signal %u", pid, WTERMSIG(rc)); else Log (4, "rc(%u)=%u", pid, WEXITSTATUS(rc)); } #endif close(sockfd); } else { del_socket(sockfd); soclose (sockfd); } return 1; }