static int _socks4_tryconnect(int clisock, struct sockaddr_in *rem_in, struct socks4_hdr *hdr4, struct conndesc *conn) { struct addrinfo *ai; int ret, sock = -1; if (hdr4->cd != SOCKS4_CD_CONNECT) goto fail_reply; if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) return (-1); if ((ai = conn->bind_ai) != NULL) { if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { warnv(0, "bind()"); return (-1); } } if (connect(sock, (struct sockaddr *)rem_in, sizeof(*rem_in)) == -1) { warnv(0, "connect()"); hdr4->cd = SOCKS4_REP_REJECT; } else { hdr4->cd = SOCKS4_REP_SUCCESS; } fail_reply: hdr4->vn = 0; if (hdr4->cd == SOCKS4_CD_RESOLVE) { /* * This is a successfull resolve, we need to set the * return CD to grant. */ ret = NET_NOPROXY; hdr4->cd = SOCKS4_REP_SUCCESS; } else if (hdr4->cd != SOCKS4_REP_SUCCESS) { ret = NET_FAIL; } else { ret = sock; } if (atomicio(write, clisock, hdr4, sizeof(*hdr4)) != sizeof(*hdr4)) ret = NET_FAIL; if (ret < 0) close(sock); /* * When we've done a resolve now (and it's successfull), we * want to return GRANT */ return (ret); }
static int socks5_bind(int clisock, struct sockaddr_in *tgt_in, struct socks5_req *req5) { struct sockaddr_in cli_in; socklen_t len; int tgtsock = -1, listensock; if ((listensock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { warnv(1, "socket()"); return (-1); } if (bind(listensock, (struct sockaddr *)tgt_in, sizeof(*tgt_in)) == -1) { warnv(1, "bind()"); goto out; } if (listen(listensock, 1) == -1) { warnv(1, "listen()"); goto out; } /* Reply: success */ req5->atyp = SOCKS5_ATYP_IPV4; req5->cd = 0; if (atomicio(write, clisock, req5, 10) != 10) goto out; len = sizeof(cli_in); if ((tgtsock = accept(listensock, (struct sockaddr *)&cli_in, &len)) == -1) goto out; req5->destaddr = cli_in.sin_addr.s_addr; req5->destport = cli_in.sin_port; /* Send second reply */ if (atomicio(write, clisock, req5, 10) != 10) { close(tgtsock); tgtsock = -1; } out: close(listensock); return (tgtsock); }
static int socks5_connect(int clisock, struct sockaddr_in *rem_in, struct socks5_req *req5, struct conndesc *conn) { int remsock; struct addrinfo *ai; /* XXX use bind_ai for socket creation, also */ if ((remsock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { warnv(0, "socket()"); return (-1); } if ((ai = conn->bind_ai) != NULL) if (bind(remsock, ai->ai_addr, ai->ai_addrlen) == -1) { warnv(0, "bind()"); return (-1); } if (connect(remsock, (struct sockaddr *)rem_in, sizeof(*rem_in)) == -1) { warnv(0, "connect()"); req5->cd = 1; } else { req5->cd = 0; } /* getpeername() */ req5->atyp = SOCKS5_ATYP_IPV4; /* XXX fill in address and port of our server (getsockname()) */ if (atomicio(write, clisock, req5, 10) != 10) { warnv(1, "write()"); goto fail; } if (req5->cd == 1) goto fail; return (remsock); fail: close(remsock); return (-1); }
int socks5_negotiate(int clisock, struct conndesc *conn) { u_int i; char hostname[256]; u_char nmethods, len, junk; struct sockaddr_in rem_in; struct socks5_req req5; struct socks5_v_repl rep5; struct hostent *hent; req5.vn = 5; req5.rsv = 0; /* * Start by retrieving number of methods, version number has * already been consumed by the calling procedure */ if (atomicio(read, clisock, &nmethods, 1) != 1) { warnv(1, "read()"); return (-1); } /* Eat up methods */ i = 0; while (i++ < nmethods) if (atomicio(read, clisock, &junk, 1) != 1) { warnv(1, "read()"); return (-1); } /* * We don't support any authentication methods yet, so simply * ignore it and send reply with no authentication required. */ rep5.ver = 5; rep5.res = 0; if (atomicio(write, clisock, &rep5, 2) != 2) { warnv(1, "write()"); return (-1); } /* Receive data up to atyp */ if (atomicio(read, clisock, &req5, 4) != 4) { warnv(1, "read()"); return (-1); } if (req5.vn != 5) return (-1); memset(&rem_in, 0, sizeof(rem_in)); switch (req5.atyp) { case SOCKS5_ATYP_IPV4: if (atomicio(read, clisock, &req5.destaddr, 4) != 4) { warnv(1, "read()"); return (-1); } rem_in.sin_family = AF_INET; rem_in.sin_addr.s_addr = req5.destaddr; break; case SOCKS5_ATYP_FQDN: if (atomicio(read, clisock, &len, 1) != 1) { warnv(1, "read()"); return (-1); } if (atomicio(read, clisock, hostname, len) != len) { warnv(1, "read()"); return (-1); } hostname[len] = '\0'; if ((hent = gethostbyname(hostname)) == NULL) { /* XXX no hstrerror() on solaris */ #ifndef __sun__ warnxv(1, "gethostbyname(): %s", hstrerror(h_errno)); #endif /* __sun__ */ return (-1); } rem_in.sin_family = AF_INET; rem_in.sin_addr = *(struct in_addr *)hent->h_addr; break; default: return (-1); } if (atomicio(read, clisock, &req5.destport, 2) != 2) { warnv(1, "read()"); return (-1); } rem_in.sin_port = req5.destport; /* * Now we have a filled in in_addr for the target host: * target_in, no socket yet. This is provided by the command * specific functions multiplexed in the next switch * statement. */ switch (req5.cd) { case SOCKS5_CD_CONNECT: return (socks5_connect(clisock, &rem_in, &req5, conn)); case SOCKS5_CD_BIND: signal_setup(); event_dispatch(); return (socks5_bind(clisock, &rem_in, &req5)); case SOCKS5_CD_UDP_ASSOC: default: return (-1); } }