static void destroyTunnel(TunnelPtr tunnel) { assert(tunnel->fd1 < 0 && tunnel->fd2 < 0); releaseAtom(tunnel->hostname); if(tunnel->buf1.buf) dispose_chunk(tunnel->buf1.buf); if(tunnel->buf2.buf) dispose_chunk(tunnel->buf2.buf); free(tunnel); }
static void fillSpecialObject(ObjectPtr object, void (*fn) (FILE *, char *), void *closure) { FILE *tmp = NULL; char *buf = NULL; int rc, len, offset; if (object->flags & OBJECT_INPROGRESS) return; buf = get_chunk(); if (buf == NULL) { abortObject(object, 503, internAtom("Couldn't allocate chunk")); goto done; } tmp = tmpfile(); if (tmp == NULL) { abortObject(object, 503, internAtom(pstrerror(errno))); goto done; } (*fn) (tmp, closure); fflush(tmp); rewind(tmp); offset = 0; while (1) { len = fread(buf, 1, CHUNK_SIZE, tmp); if (len <= 0 && ferror(tmp)) { abortObject(object, 503, internAtom(pstrerror(errno))); goto done; } if (len <= 0) break; rc = objectAddData(object, buf, offset, len); if (rc < 0) { abortObject(object, 503, internAtom("Couldn't add data to object")); goto done; } offset += len; } object->length = offset; done: if (buf) dispose_chunk(buf); if (tmp) fclose(tmp); notifyObject(object); }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { int n; assert(buf); (void)offset; (void)len; n = httpWriteErrorHeaders(buf, CHUNK_SIZE, 0, 1, 501, internAtom("CONNECT not available " "in this version."), 1, NULL, url->string, url->length, NULL); releaseAtom(url); if (n >= 0) { write(fd, buf, n); } dispose_chunk(buf); lingeringClose(fd); return; }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { int n; assert(buf); n = httpWriteErrorHeaders(buf, CHUNK_SIZE, 0, 1, 501, internAtom("CONNECT not available " "in this version."), 1, NULL, url->string, url->length, NULL); releaseAtom(url); if(n >= 0) { /* This is completely wrong. The write is non-blocking, and we don't reschedule it if it fails. But then, if the write blocks, we'll simply drop the connection with no error message. */ write(fd, buf, n); } dispose_chunk(buf); lingeringClose(fd); return; }
void initHttp() { char *buf = NULL; int namelen; int n; struct hostent *host; initHttpParser(); atom100Continue = internAtom("100-continue"); if(clientTimeout <= serverTimeout) { clientTimeout = serverTimeout + 1; do_log(L_WARN, "Value of clientTimeout too small -- setting to %d.\n", clientTimeout); } if(displayName == NULL) displayName = internAtom("Polipo"); if(authCredentials != NULL && authRealm == NULL) authRealm = internAtom("Polipo"); if(allowedClients) { allowedNets = parseNetAddress(allowedClients); if(allowedNets == NULL) exit(1); } if(allowedPorts == NULL) { allowedPorts = makeIntList(0); if(allowedPorts == NULL) { do_log(L_ERROR, "Couldn't allocate allowedPorts.\n"); exit(1); } intListCons(80, 100, allowedPorts); intListCons(1024, 0xFFFF, allowedPorts); } if(tunnelAllowedPorts == NULL) { tunnelAllowedPorts = makeIntList(0); if(tunnelAllowedPorts == NULL) { do_log(L_ERROR, "Couldn't allocate tunnelAllowedPorts.\n"); exit(1); } intListCons(22, 22, tunnelAllowedPorts); /* ssh */ intListCons(80, 80, tunnelAllowedPorts); /* HTTP */ intListCons(109, 110, tunnelAllowedPorts); /* POP 2 and 3*/ intListCons(143, 143, tunnelAllowedPorts); /* IMAP 2/4 */ intListCons(443, 443, tunnelAllowedPorts); /* HTTP/SSL */ intListCons(873, 873, tunnelAllowedPorts); /* rsync */ intListCons(993, 993, tunnelAllowedPorts); /* IMAP/SSL */ intListCons(995, 995, tunnelAllowedPorts); /* POP/SSL */ intListCons(2401, 2401, tunnelAllowedPorts); /* CVS */ intListCons(5222, 5223, tunnelAllowedPorts); /* Jabber */ intListCons(9418, 9418, tunnelAllowedPorts); /* Git */ } if(proxyName) return; buf = get_chunk(); if(buf == NULL) { do_log(L_ERROR, "Couldn't allocate chunk for host name.\n"); goto fail; } n = gethostname(buf, CHUNK_SIZE); if(n != 0) { do_log_error(L_WARN, errno, "Gethostname"); strcpy(buf, "polipo"); goto success; } /* gethostname doesn't necessarily NUL-terminate on overflow */ buf[CHUNK_SIZE - 1] = '\0'; if(strcmp(buf, "(none)") == 0 || strcmp(buf, "localhost") == 0 || strcmp(buf, "localhost.localdomain") == 0) { do_log(L_WARN, "Couldn't determine host name -- using ``polipo''.\n"); strcpy(buf, "polipo"); goto success; } if(strchr(buf, '.') != NULL) goto success; host = gethostbyname(buf); if(host == NULL) { goto success; } if(host->h_addrtype != AF_INET) goto success; host = gethostbyaddr(host->h_addr_list[0], host->h_length, AF_INET); if(!host || !host->h_name || strcmp(host->h_name, "localhost") == 0 || strcmp(host->h_name, "localhost.localdomain") == 0) goto success; namelen = strlen(host->h_name); if(namelen >= CHUNK_SIZE) { do_log(L_ERROR, "Host name too long.\n"); goto success; } memcpy(buf, host->h_name, namelen + 1); success: proxyName = internAtom(buf); if(proxyName == NULL) { do_log(L_ERROR, "Couldn't allocate proxy name.\n"); goto fail; } dispose_chunk(buf); return; fail: if(buf) dispose_chunk(buf); exit(1); return; }
int specialRequestHandler(int status, FdEventHandlerPtr event, StreamRequestPtr srequest) { SpecialRequestPtr request = srequest->data; int rc; int killed = 0; (void)event; if (status < 0) { kill(request->pid, SIGTERM); killed = 1; request->object->flags &= ~OBJECT_INPROGRESS; abortObject(request->object, 502, internAtomError(-status, "Couldn't read from client")); goto done; } if (srequest->offset > 0) { rc = objectAddData(request->object, request->buf, request->offset, srequest->offset); if (rc < 0) { kill(request->pid, SIGTERM); killed = 1; request->object->flags &= ~OBJECT_INPROGRESS; abortObject(request->object, 503, internAtom ("Couldn't add data to connection")); goto done; } request->offset += srequest->offset; } if (status) { request->object->flags &= ~OBJECT_INPROGRESS; request->object->length = request->object->size; goto done; } if (request->object->refcount <= 1) { kill(request->pid, SIGTERM); killed = 1; request->object->flags &= ~OBJECT_INPROGRESS; abortObject(request->object, 500, internAtom("Aborted")); goto done; } notifyObject(request->object); do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, CHUNK_SIZE, specialRequestHandler, request); return 1; done: close(request->fd); dispose_chunk(request->buf); releaseNotifyObject(request->object); do { rc = waitpid(request->pid, &status, 0); } while (rc < 0 && errno == EINTR); if (rc < 0) { do_log(L_ERROR, "Wait for %d: %d\n", (int)request->pid, errno); } else { int normal = (WIFEXITED(status) && WEXITSTATUS(status) == 0) || (killed && WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM); char *reason = WIFEXITED(status) ? "with status" : WIFSIGNALED(status) ? "on signal" : "with unknown status"; int value = WIFEXITED(status) ? WEXITSTATUS(status) : WIFSIGNALED(status) ? WTERMSIG(status) : status; do_log(normal ? D_CHILD : L_ERROR, "Child %d exited %s %d.\n", (int)request->pid, reason, value); } free(request); return 1; }
static void tunnelDispatch(TunnelPtr tunnel) { if(circularBufferEmpty(&tunnel->buf1)) { if(tunnel->buf1.buf && !(tunnel->flags & (TUNNEL_READER1 | TUNNEL_WRITER2))) { dispose_chunk(tunnel->buf1.buf); tunnel->buf1.buf = NULL; tunnel->buf1.head = tunnel->buf1.tail = 0; } } if(circularBufferEmpty(&tunnel->buf2)) { if(tunnel->buf2.buf && !(tunnel->flags & (TUNNEL_READER2 | TUNNEL_WRITER1))) { dispose_chunk(tunnel->buf2.buf); tunnel->buf2.buf = NULL; tunnel->buf2.head = tunnel->buf2.tail = 0; } } if(tunnel->fd1 >= 0) { if(!(tunnel->flags & (TUNNEL_READER1 | TUNNEL_EOF1)) && !circularBufferFull(&tunnel->buf1)) { tunnel->flags |= TUNNEL_READER1; bufRead(tunnel->fd1, &tunnel->buf1, tunnelRead1Handler, tunnel); } if(!(tunnel->flags & (TUNNEL_WRITER1 | TUNNEL_EPIPE1)) && !circularBufferEmpty(&tunnel->buf2)) { tunnel->flags |= TUNNEL_WRITER1; /* There's no IO_NOTNOW in bufWrite, so it might close the file descriptor straight away. Wait until we're rescheduled. */ bufWrite(tunnel->fd1, &tunnel->buf2, tunnelWrite1Handler, tunnel); return; } if(tunnel->fd2 < 0 || (tunnel->flags & TUNNEL_EOF2)) { if(!(tunnel->flags & TUNNEL_EPIPE1)) shutdown(tunnel->fd1, 1); tunnel->flags |= TUNNEL_EPIPE1; } else if(tunnel->fd1 < 0 || (tunnel->flags & TUNNEL_EPIPE2)) { if(!(tunnel->flags & TUNNEL_EOF1)) shutdown(tunnel->fd1, 0); tunnel->flags |= TUNNEL_EOF1; } if((tunnel->flags & TUNNEL_EOF1) && (tunnel->flags & TUNNEL_EPIPE1)) { if(!(tunnel->flags & (TUNNEL_READER1 | TUNNEL_WRITER1))) { CLOSE(tunnel->fd1); tunnel->fd1 = -1; } } } if(tunnel->fd2 >= 0) { if(!(tunnel->flags & (TUNNEL_READER2 | TUNNEL_EOF2)) && !circularBufferFull(&tunnel->buf2)) { tunnel->flags |= TUNNEL_READER2; bufRead(tunnel->fd2, &tunnel->buf2, tunnelRead2Handler, tunnel); } if(!(tunnel->flags & (TUNNEL_WRITER2 | TUNNEL_EPIPE2)) && !circularBufferEmpty(&tunnel->buf1)) { tunnel->flags |= TUNNEL_WRITER2; bufWrite(tunnel->fd2, &tunnel->buf1, tunnelWrite2Handler, tunnel); return; } if(tunnel->fd1 < 0 || (tunnel->flags & TUNNEL_EOF1)) { if(!(tunnel->flags & TUNNEL_EPIPE2)) shutdown(tunnel->fd2, 1); tunnel->flags |= TUNNEL_EPIPE2; } else if(tunnel->fd1 < 0 || (tunnel->flags & TUNNEL_EPIPE1)) { if(!(tunnel->flags & TUNNEL_EOF2)) shutdown(tunnel->fd2, 0); tunnel->flags |= TUNNEL_EOF2; } if((tunnel->flags & TUNNEL_EOF2) && (tunnel->flags & TUNNEL_EPIPE2)) { if(!(tunnel->flags & (TUNNEL_READER2 | TUNNEL_WRITER2))) { CLOSE(tunnel->fd2); tunnel->fd2 = -1; } } } if(tunnel->fd1 < 0 && tunnel->fd2 < 0) destroyTunnel(tunnel); else assert(tunnel->flags & (TUNNEL_READER1 | TUNNEL_WRITER1 | TUNNEL_READER2 | TUNNEL_WRITER2)); }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { TunnelPtr tunnel; int port; char *p, *q; tunnel = makeTunnel(fd, buf, offset, len); if(tunnel == NULL) { do_log(L_ERROR, "Couldn't allocate tunnel.\n"); releaseAtom(url); dispose_chunk(buf); CLOSE(fd); return; } if(proxyOffline) { do_log(L_INFO, "Attemted CONNECT when disconnected.\n"); releaseAtom(url); tunnelError(tunnel, 502, internAtom("Cannot CONNECT when disconnected.")); return; } p = memrchr(url->string, ':', url->length); q = NULL; if(p) port = strtol(p + 1, &q, 10); if(!p || q != url->string + url->length) { do_log(L_ERROR, "Couldn't parse CONNECT.\n"); releaseAtom(url); tunnelError(tunnel, 400, internAtom("Couldn't parse CONNECT")); return; } tunnel->hostname = internAtomLowerN(url->string, p - url->string); if(tunnel->hostname == NULL) { releaseAtom(url); tunnelError(tunnel, 501, internAtom("Couldn't allocate hostname")); return; } if(!intListMember(port, tunnelAllowedPorts)) { releaseAtom(url); tunnelError(tunnel, 403, internAtom("Forbidden port")); return; } tunnel->port = port; if (tunnelIsMatched(url->string, url->length, tunnel->hostname->string, tunnel->hostname->length)) { releaseAtom(url); tunnelError(tunnel, 404, internAtom("Forbidden tunnel")); logTunnel(tunnel,1); return; } logTunnel(tunnel,0); releaseAtom(url); if(socksParentProxy) do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); else do_gethostbyname(parentHost ? parentHost->string : tunnel->hostname->string, 0, tunnelDnsHandler, tunnel); }
void do_tunnel(int fd, char *buf, int offset, int len, AtomPtr url) { TunnelPtr tunnel; int port; char *p, *q; /* PSIPHON */ if(psiphonStats) { /* Update the page view stats by printf-ing the URI. Our stdout is piped to the client process. */ printf("PSIPHON-PAGE-VIEW-HTTPS:>>%s<<\n", url->string); fflush(NULL); } /* /PSIPHON */ tunnel = makeTunnel(fd, buf, offset, len); if(tunnel == NULL) { do_log(L_ERROR, "Couldn't allocate tunnel.\n"); releaseAtom(url); dispose_chunk(buf); CLOSE(fd); return; } if(proxyOffline) { do_log(L_INFO, "Attemted CONNECT when disconnected.\n"); releaseAtom(url); tunnelError(tunnel, 502, internAtom("Cannot CONNECT when disconnected.")); return; } p = memrchr(url->string, ':', url->length); q = NULL; if(p) port = strtol(p + 1, &q, 10); if(!p || q != url->string + url->length) { do_log(L_ERROR, "Couldn't parse CONNECT.\n"); releaseAtom(url); tunnelError(tunnel, 400, internAtom("Couldn't parse CONNECT")); return; } tunnel->hostname = internAtomLowerN(url->string, p - url->string); if(tunnel->hostname == NULL) { releaseAtom(url); tunnelError(tunnel, 501, internAtom("Couldn't allocate hostname")); return; } /* PSIPHON Checking if tunnel is allowed on a particular port is not needed if the proxy accepts connections made only from localhost */ /* if(!intListMember(port, tunnelAllowedPorts)) { releaseAtom(url); tunnelError(tunnel, 403, internAtom("Forbidden port")); return; } */ /* /PSIPHON */ tunnel->port = port; if (tunnelIsMatched(url->string, url->length, tunnel->hostname->string, tunnel->hostname->length)) { releaseAtom(url); tunnelError(tunnel, 404, internAtom("Forbidden tunnel")); logTunnel(tunnel,1); return; } logTunnel(tunnel,0); releaseAtom(url); /* PSIPHON split tunneling option*/ /* This was the original: if(socksParentProxy) do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); */ if(socksParentProxy) { if(splitTunneling) { do_gethostbyname_socks(parentHost ? parentHost->string : tunnel->hostname->string, 0, tunnelSplitTunnelingDnsHandler, tunnel); } else { do_socks_connect(parentHost ? parentHost->string : tunnel->hostname->string, parentHost ? parentPort : tunnel->port, tunnelSocksHandler, tunnel); } } /* /PSIPHON */ else do_gethostbyname(parentHost ? parentHost->string : tunnel->hostname->string, 0, tunnelDnsHandler, tunnel); }