/* $Id: emuProxy2.c,v 1.4 2005/10/13 23:32:14 tym Exp tym $ */ /* Tivo emuProxy * Tim Kleingeld, 2005 * Version 2.1 - add Content-Length header if none is present for post requests * Version 2.2 - added magic header options for power users... * Version 3.1 - added Warren's patch to move the space in 'HTTP/1.0 CR' */ char *version = "3.1"; #include #include #include #include #include #include #include #include #include #include #define LISTENTO "0.0.0.0" /* Useful for testing */ #define PORT 8000 #define SERVERIP "131.244.9.101" #define SERVERPORT 80 /* #define LISTENTO "127.0.0.1" */ /* Potentially more secure */ #define MAX_MAGIC 20 int gotAlarm; char myHeaders[1024]; char proxystuff[1024]; int debug; int localport = PORT; char *serverip = SERVERIP; int serverport = SERVERPORT; char *proxyip = 0; int proxyport = 3128; char *listento = "0.0.0.0"; char *id = "$Id: emuProxy2.c,v 1.4 2005/10/13 23:32:14 tym Exp tym $"; char *magicHeaders[MAX_MAGIC]; int magicCount = 0; void alarmHandler(int sig) { gotAlarm = 1; } int main(int argc, char **argv) { int listener = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; int reuse = 1; int arg = 1; while (arg < argc) { if (!strcmp(argv[arg], "-d")) { debug = 1; arg++; } else if (!strcmp(argv[arg], "-l")) { localport = atoi(argv[arg+1]); arg+=2; } else if (!strcmp(argv[arg], "-H")) { if (magicCount == MAX_MAGIC) { fprintf(stderr, "Maximum addition headers reached (%d). Aborting\n", magicCount); exit(1); } magicHeaders[magicCount++] = argv[arg+1]; arg+=2; } else if (!strcmp(argv[arg], "-L")) { listento = argv[arg+1]; arg+=2; } else if (!strcmp(argv[arg], "--proxy-ip")) { proxyip = argv[arg+1]; arg+=2; } else if (!strcmp(argv[arg], "-s")) { serverip = argv[arg+1]; arg+=2; } else if (!strcmp(argv[arg], "-v")) { fprintf(stderr, "emuProxy Version: %s\n", version); exit(0); } else if (!strcmp(argv[arg], "-p")) { serverport = atoi(argv[arg+1]); arg+=2; } else if (!strcmp(argv[arg], "--proxy-port")) { proxyport = atoi(argv[arg+1]); arg+=2; } else { if (strcmp(argv[arg], "-h")) fprintf(stderr, "Unrecognised argument '%s'\n", argv[arg]); fprintf(stderr, "emuProxy Version: %s\n", version); fprintf(stderr, "Usage: %s [-d] [-h] [-l localport] [-L listenAddr] [-s serverip]\n", argv[0]); fprintf(stderr, " [-p serverport] [--proxy-ip proxyip] [--proxy-port proxyport]\n"); fprintf(stderr, " [-H headerline]*\n"); exit(1); } } sprintf(myHeaders, "Host: %s\r\nTivoURLBase: http://127.0.0.1:%d/\r\n", SERVERIP, localport); addr.sin_family = AF_INET; if (listener < 0) { perror("Cannot create socket"); exit(1); } if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse) < 0) { perror("Cannot set reuse addr option"); exit(1); } addr.sin_addr.s_addr = inet_addr(listento); addr.sin_port = htons(localport); if (bind(listener, (struct sockaddr *)&addr, sizeof addr) < 0) { perror("Cannot bind port - are we already running?"); exit(1); } if (listen(listener, 5)< 0) { perror("Cannot listener on socket?"); exit(1); } /* Treat SIGPIPE like a timeout - one end has gone away */ signal(SIGPIPE, &alarmHandler); signal(SIGALRM, &alarmHandler); /* Fill in the server address */ if (proxyip) { addr.sin_addr.s_addr = inet_addr(proxyip); addr.sin_port = htons(proxyport); sprintf(proxystuff, "http://%s:%d", serverip, serverport); } else { addr.sin_addr.s_addr = inet_addr(serverip); addr.sin_port = htons(serverport); } /* Main handling starts here. We only handle a single connection at a time * if you need more, fix it! */ while (1) { int server = -1; int client; int used = 0; /* Number of characters left in the buffer from last read */ int offset = 0; /* Characters we've already processed */ int doneheaders=0; /* Have we output the new headers */ int foundend = 0; /* Have we found the blank line yet */ int bodylength = -1; /* Remember content length value */ char *http10; /* Pointer to occurrence of "HTTP/1.0 CR" */ int linecount = 0; /* How many lines have we processed */ int post = 0; /* Is this a post request? */ char buffer[1024]; int r; alarm(0); gotAlarm = 0; client = accept(listener, 0, 0); if (client < 0) { /* Weird... were we interrupted maybe? */ sleep(1); continue; } if (debug) fprintf(stderr, "Accepted connection\n"); server = socket(PF_INET, SOCK_STREAM, 0); if (server < 0) { perror("Could not create socket"); close(client); continue; } if (connect(server, (struct sockaddr *) &addr, sizeof addr) < 0) { perror("Could not connect to server"); close(client); close(server); continue; } if (debug) fprintf(stderr, "Connected to %s\n", serverip); alarm(15); /* Primitive timeout handling */ while (!foundend) { /* Read header section */ r = read(client, buffer + used, (sizeof buffer) - 1-used); if (r <= 0 || gotAlarm) { /* Error or EOF... I wonder what happened */ close(client); close(server); gotAlarm = 0; server = -1; break; } used += r; buffer[used] = 0; /* Null terminate for string functions */ if (debug) fprintf(stderr, "read %d: '%s'\n", r, buffer); /* Replace any "HTTP/1.0 CR" with " HTTP/1.0CR */ if (!linecount && (http10 = strstr(buffer + offset, "HTTP/1.0 \015")) != NULL) memcpy(http10, " HTTP/1.0\015", 10); while (offset < used && !foundend) { int newoffset; char *newline = strchr(buffer + offset, '\n'); if (debug) fprintf(stderr,"offset %d used %d '%s'\n", offset, used, buffer+offset); if (!newline) { if (debug) fprintf(stderr, "incomplete line\n"); /* Incomplete line */ if (offset) { memcpy(buffer, buffer+offset, used-offset+1); used -= offset; offset = 0; } else if (used > (sizeof buffer) - 2) { /* Buffer is full! just send it through */ write(server, buffer, used); used = 0; } break; } newoffset = newline - buffer; /* Detect post requests */ if (!linecount && !strncasecmp("POST", buffer+offset, 4)) { post = 1; } /* Detect blank line at end of headers */ if (newoffset == offset || (newoffset == offset + 1 && buffer[offset] == '\r')) { /* Blank line found! */ foundend = 1; /* Output a content length for post requests if there has been * none */ if (post && bodylength < 0) { char *s = "Content-Length: 0\n"; write(server, s, strlen(s)); } } if (!linecount && proxyip) { char *slash = strchr(buffer+offset, '/'); if (slash) { int slashoff = slash - buffer - offset; write(server, buffer+offset, slashoff); write(server, proxystuff, strlen(proxystuff)); write(server, slash, newoffset - offset - slashoff + 1); } else { write(server, buffer+offset, newoffset-offset+1); } } else { write(server, buffer+offset, newoffset-offset+1); } linecount++; newoffset++; if (foundend) { offset = newoffset; if (used > offset) { write(server, buffer+offset, used-offset); bodylength -= (used - offset); } break; } if (!doneheaders) { int i; write(server, myHeaders, strlen(myHeaders)); if (debug) fprintf(stderr, "Send my headers '%s'\n", myHeaders); doneheaders = 1; for (i = 0; i < magicCount; i++) { write(server, magicHeaders[i], strlen(magicHeaders[i])); write(server, "\r\n", 2); } } if (!strncasecmp(buffer+offset, "Content-length:", 15)) { bodylength = atoi(buffer+offset+15); } offset = newoffset; } if (offset == used) offset = used = 0; } if (server < 0) continue; /* First we pass out post data */ while (bodylength > 0) { int len = bodylength; if (len > sizeof buffer) len = sizeof buffer; r = read(client, buffer, len); if (r <= 0 || gotAlarm) { /* Error of some sort... */ close(client); close(server); gotAlarm = 0; server = -1; break; } write(server, buffer, r); bodylength -= r; } if (server < 0) continue; /* Now we pass back the reply */ while (1) { alarm(15); /* Just in case */ r = read(server, buffer, sizeof buffer); if (r <= 0 || gotAlarm) break; /* Either EOF or error - just quit */ write(client, buffer, r); } close(client); close(server); } }