📄 sockd_request.c
字号:
p = 1; if (setsockopt(out, SOL_SOCKET, SO_REUSEADDR, &p, sizeof(p)) != 0) swarn("%s: setsockopt(SO_REUSEADDR)", function); } if (PORTISRESERVED(bound.sin_port) && config.compat.sameport) { uid_t euid; socks_seteuid(&euid, config.uid.privileged); p = bindresvport(out, &bound); socks_reseteuid(config.uid.privileged, euid); } else /* LINTED pointer casts may be troublesome */ p = sockd_bind(out, (struct sockaddr *)&bound, 1); if (p != 0) { /* no such luck, bind any port and let client decide if ok. */ bound.sin_port = htons(0); /* LINTED pointer casts may be troublesome */ if ((p = sockd_bind(out, (struct sockaddr *)&bound, 0)) != 0) swarn("%s: bind(%s)", /* LINTED pointer casts may be troublesome */ function, sockaddr2string((struct sockaddr *)&bound, a, sizeof(a))); } if (p != 0) { send_failure(request->s, &response, errno2reply(errno, response.version)); close(request->s); close(out); return; } /* rules permit? */ switch (request->req.command) { case SOCKS_BIND: { struct sockshost_t boundhost; socklen_t boundlen; boundlen = sizeof(bound); /* LINTED pointer casts may be troublesome */ if (getsockname(out, (struct sockaddr *)&bound, &boundlen) != 0) { swarn("%s: getsockname(out)", function); close(request->s); close(out); return; } /* LINTED pointer casts may be troublesome */ sockaddr2sockshost((struct sockaddr *)&bound, &boundhost); permit = rulespermit(request->s, &io.rule, &io.state, &io.src, &boundhost); iolog(&io.rule, &io.state, OPERATION_CONNECT, &io.src, &boundhost, NULL, 0); break; } case SOCKS_CONNECT: permit = rulespermit(request->s, &io.rule, &io.state, &io.src, &io.dst); iolog(&io.rule, &io.state, OPERATION_CONNECT, &io.src, &io.dst, NULL, 0); break; case SOCKS_UDPASSOCIATE: { struct sockshost_t *src; struct connectionstate_t replystate; /* * Client is allowed to send a "incomplete" address. */ if (io.src.atype == SOCKS_ADDR_IPV4 && (io.src.addr.ipv4.s_addr == htonl(0) || io.src.port == htons(0))) src = NULL; else src = &io.src; /* only set temporary here for one replypacket at a time. */ replystate = io.state; replystate.command = SOCKS_UDPREPLY; /* one direction is atleast in theory good enough. */ permit = rulespermit(request->s, &io.rule, &io.state, src, NULL) || rulespermit(request->s, &io.rule, &replystate, NULL, src); iolog(&io.rule, &io.state, OPERATION_CONNECT, &io.src, &io.dst, NULL, 0); break; } default: SERRX(request->req.command); } if (!permit) { send_failure(request->s, &response, SOCKS_NOTALLOWED); close(request->s); close(out); return; } /* * Set up missing bits of io and send it to mother. */ switch (io.state.command) { case SOCKS_BIND: { struct sockd_io_t *iolist; struct sockd_io_t bindio; /* send this to proxyrelayer. */ struct sockaddr boundaddr; /* address we listen on. */ struct sockaddr clientaddr; /* clientaddress we forward to. */ socklen_t len; int flags, emfile; enum socketindex { client, childpipe, ourpipe, reply, remote }; /* array of sockets, indexed by above enums, -1 if not open. */ int sv[(int)(remote) + 1] = { -1, -1, -1, -1, -1 }; SASSERTX(sv[ELEMENTS(sv) - 1] == -1); sv[client] = request->s; if (listen(out, 5) != 0) { swarn("%s: listen(out)", function); send_failure(sv[client], &response, SOCKS_FAILURE); closev(sv, ELEMENTS(sv)); break; } /* for accept(). */ if ((flags = fcntl(out, F_GETFL, 0)) == -1 || fcntl(out, F_SETFL, flags | O_NONBLOCK) == -1) { swarn("%s: fcntl()", function); send_failure(sv[client], &response, SOCKS_FAILURE); closev(sv, ELEMENTS(sv)); break; } len = sizeof(boundaddr); if (getsockname(out, &boundaddr, &len) != 0) { swarn("%s: getsockname()", function); send_failure(sv[client], &response, SOCKS_FAILURE); closev(sv, ELEMENTS(sv)); break; } sockaddr2sockshost(&boundaddr, &response.host); response.reply = (char)sockscode(response.version, SOCKS_SUCCESS); /* LINTED pointer casts may be troublesome */ clientaddr = *(const struct sockaddr *)&request->from; if (io.state.extension.bind) { int pipev[2]; /* * The problem is that both we and the process which receives * the io packet needs to know when the client closes it's * connection, but _we_ need to receive a query from the * client on the connection aswell, and the io process would * get confused about that. We try to hack around that * by making a "dummy" descriptor that the io process can * check as all other controlconnections and which we * can close when the client closes the real controlconnection, * so the io process can detect it. Not very nice, no. */ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipev) != 0) { swarn("%s: socketpair()", function); send_failure(sv[client], &response, SOCKS_FAILURE); closev(sv, ELEMENTS(sv)); break; } sv[childpipe] = pipev[0]; sv[ourpipe] = pipev[1]; /* LINTED pointer casts may be troublesome */ ((struct sockaddr_in *)&clientaddr)->sin_port = io.dst.port; } /* let client know what address we bound to on it's behalf. */ if (send_response(sv[client], &response) != 0) { iolog(&io.rule, &io.state, OPERATION_ABORT, &io.src, &response.host, NULL, 0); closev(sv, ELEMENTS(sv)); break; } setproctitle("bindrelayer: %s -> %s", sockaddr2string(&boundaddr, a, sizeof(a)), sockaddr2string(&clientaddr, b, sizeof(b))); /* * regardless of what kind of bind semantics are in use, * portnumber is something we ignore when checking remote peer. */ io.dst.port = htons(0); emfile = 0; iolist = NULL; /* CONSTCOND */ while (1) { struct ruleaddress_t ruleaddr; struct sockaddr remoteaddr; /* remote address we accepted. */ struct sockshost_t remotehost; /* remote address, sockhost form.*/ struct sockaddr_in replyaddr; /* address of bindreply socket. */ int fdbits = -1; fd_set rset; /* some sockets change, most remain the same. */ sv[reply] = -1; sv[remote] = -1; FD_ZERO(&rset); FD_SET(sv[client], &rset); fdbits = MAX(fdbits, sv[client]); if (!emfile) { FD_SET(out, &rset); fdbits = MAX(fdbits, out); } ++fdbits; if ((p = selectn(fdbits, &rset, NULL, NULL, NULL)) <= 0) SERR(p); if (FD_ISSET(sv[client], &rset)) { /* * nothing is normally expected on controlconnection so * assume it's a bind extension query or eof. */ struct request_t query; struct response_t queryresponse; struct negotiate_state_t state; struct sockaddr queryaddr; bzero(&state, sizeof(state)); bzero(&query, sizeof(query)); bzero(&queryresponse, sizeof(queryresponse)); query.auth = request->req.auth; switch (p = recv_sockspacket(sv[client], &query, &state)) { case -1: iolog(&io.rule, &io.state, OPERATION_ABORT, &io.src, &response.host, NULL, 0); break; case 0: p = -1; /* session ended. */ break; default: { struct sockd_io_t *fio; slog(LOG_DEBUG, "received request: %s", socks_packet2string(&query, SOCKS_REQUEST)); switch (query.version) { case SOCKS_V4: queryresponse.version = SOCKS_V4REPLY_VERSION; break; case SOCKS_V5: queryresponse.version = query.version; break; default: SERRX(query.version); } sockshost2sockaddr(&query.host, &queryaddr); if ((fio = io_find(iolist, &queryaddr)) == NULL) { queryresponse.host.atype = SOCKS_ADDR_IPV4; queryresponse.host.addr.ipv4.s_addr = htonl(0); queryresponse.host.port = htons(0); } else { SASSERTX(fio->state.command = SOCKS_BINDREPLY); /* LINTED pointer casts may be troublesome */ SASSERTX(sockaddrareeq((struct sockaddr *) &fio->in.laddr, &queryaddr)); /* LINTED pointer casts may be troublesome */ sockaddr2sockshost((struct sockaddr *)&fio->out.raddr, &queryresponse.host); } if (fio != NULL) { flushio(mother, sv[client], &queryresponse, fio); emfile = MAX(0, emfile - 3); /* flushio() closes 3. */ iolist = io_remove(iolist, fio); p = 0; } else if ((p = send_response(sv[client], &queryresponse)) != 0) iolog(&io.rule, &io.state, OPERATION_ABORT, &io.src, &response.host, NULL, 0); } } if (p != 0) break; } if (!FD_ISSET(out, &rset)) continue; len = sizeof(remoteaddr); if ((sv[remote] = acceptn(out, &remoteaddr, &len)) == -1) { swarn("%s: accept(out)", function); switch (errno) {#ifdef EPROTO case EPROTO: /* overloaded SVR4 error */#endif case EWOULDBLOCK: /* BSD */ case ECONNABORTED: /* POSIX */ /* rest appears to be linux stuff according to apache src. */#ifdef ECONNRESET case ECONNRESET:#endif#ifdef ETIMEDOUT case ETIMEDOUT:#endif#ifdef EHOSTUNREACH case EHOSTUNREACH:#endif#ifdef ENETUNREACH case ENETUNREACH:#endif continue; case EMFILE: case ENFILE: ++emfile; continue; } break; /* errno is not ok. */ } sockaddr2sockshost(&remoteaddr, &remotehost); /* accepted connection; does remote address match requested? */ if (io.state.extension.bind || addressmatch(sockshost2ruleaddress(&io.dst, &ruleaddr), &remotehost, SOCKS_TCP, 1)) { bindio = io; /* quick init of most stuff. */ bindio.src = remotehost; sockaddr2sockshost(&clientaddr, &bindio.dst); bindio.state.command = SOCKS_BINDREPLY; permit = rulespermit(sv[client], &bindio.rule, &bindio.state, &bindio.src, &bindio.dst); iolog(&bindio.rule, &bindio.state, OPERATION_CONNECT, &bindio.src, &bindio.dst, NULL, 0); } else { slog(LOG_INFO, "%s(0): unexpected bindreply: %s -> %s", VERDICT_BLOCKs, sockaddr2string(&remoteaddr, a, sizeof(a)), sockshost2string(&io.src, b, sizeof(b))); permit = 0; } if (!permit) { close(sv[remote]); continue; /* wait for next connect, but will there be one? */ } /* * Someone connected to socket we listen to on behalf of client. * If we are using the bind extension, connect to address client * is listening on. Otherwise, send the data on the connection * we already have. */ if (bindio.state.extension.bind) { if ((sv[reply] = socket(AF_INET, SOCK_STREAM, 0)) == -1) { swarn("%s: socket(SOCK_STREAM)", function); switch (errno) { case EMFILE: case ENFILE: ++emfile; /* FALLTHROUGH */ case ENOBUFS: close(sv[remote]); continue; } break; /* errno is not ok. */ } setsockoptions(sv[reply]); /* LINTED pointer casts may be troublesome */ replyaddr = *(struct sockaddr_in *)&boundaddr; replyaddr.sin_port = htons(0); /* LINTED pointer casts may be troublesome */ if (sockd_bind(sv[reply], (struct sockaddr *)&replyaddr, 0) != 0) { /* LINTED pointer casts may be troublesome */ swarn("%s: bind(%s)", function, sockaddr2string((struct sockaddr *)&replyaddr, a, sizeof(a))); break; } len = sizeof(replyaddr); /* LINTED pointer casts may be troublesome */ if (getsockname(sv[reply], (struct sockaddr *)&replyaddr, &len) != 0) { swarn("%s: getsockname(sv[reply])", function); if (errno == ENOBUFS) { close(sv[remote]); close(sv[reply]); continue; } break; } slog(LOG_DEBUG, "connecting to %s", sockaddr2string(&clientaddr, a, sizeof(a)));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -