/* * NNTPtime, a small program which attempts to time the propagation * of an article through NNTP servers. * * kolya@MIT.EDU */ #include #include #include #include #include #include #include #define BUFSIZE 256 pthread_mutex_t history_mutex, sender_mutex; struct hist_ent { struct hist_ent *next; char msgid[BUFSIZE]; struct timeval t_snd; int refcount; } *history; struct hist_ent *history_lookup(char *msgid) { struct hist_ent *he; pthread_mutex_lock(&history_mutex); he=history; while((he!=NULL) && (strcmp(msgid, he->msgid))) he=he->next; pthread_mutex_unlock(&history_mutex); return he; } void history_release(struct hist_ent *he) { pthread_mutex_lock(&history_mutex); he->refcount--; pthread_mutex_unlock(&history_mutex); } struct hist_ent *history_newent(void) { struct hist_ent *he; he=(struct hist_ent *)malloc(sizeof(struct hist_ent)); he->refcount=0; return he; } void *receiver(void *arg) { struct sockaddr_in sin; int r, sock = (int)arg, addrlen = sizeof(sin); char buf[BUFSIZE], tmp[BUFSIZE]; FILE *rf, *wf; getpeername(sock, (struct sockaddr *)&sin, &addrlen); printf("<<< Incoming connection %d from %d.%d.%d.%d open\n", pthread_self(), (sin.sin_addr.s_addr & 0xff000000) >> 24, (sin.sin_addr.s_addr & 0x00ff0000) >> 16, (sin.sin_addr.s_addr & 0x0000ff00) >> 8, (sin.sin_addr.s_addr & 0x000000ff)); rf=fdopen(sock, "r"); setbuf(rf, NULL); wf=fdopen(sock, "w"); setbuf(wf, NULL); fprintf(wf, "200 greetings and salutations.\r\n"); while(fgets(buf, sizeof(buf), rf)) { if(!strncasecmp(buf, "mode stream", 11)) { fprintf(wf, "203 sure, dude.\r\n"); } else if(!strncasecmp(buf, "check", 5)) { char *mid, *term; int res; struct hist_ent *he; struct timeval t_rcv; mid=strchr(buf, ' '); 0[mid++]=0; term=strchr(mid, '\r'); 0[term]=0; he=history_lookup(mid); if(he) { printf("<<< Accepting CHECK %s\n", mid); fprintf(wf, "238 %s\r\n", mid); gettimeofday(&t_rcv, NULL); printf("||| IHAVE-to-CHECK delay: %d usec\n", (t_rcv.tv_usec-he->t_snd.tv_usec)+ 1000000*(t_rcv.tv_sec-he->t_snd.tv_sec)); history_release(he); } else { printf("<<< Rejecting CHECK %s\n", mid); fprintf(wf, "438 %s\r\n", mid); } } else if(!strncasecmp(buf, "quit", 4)) { fprintf(wf, "205 later, guy.\r\n"); close(sock); } else if(!strncasecmp(buf, "takethis", 8)) { char *mid, *term; int in_progress=1; mid=strchr(buf, ' '); 0[mid++]=0; term=strchr(mid, '\r'); 0[term]=0; while(in_progress) { int pos; r=(int)fgets(tmp, sizeof(tmp), rf); pos=strlen(tmp)-1; while(tmp[pos]=='\r' || tmp[pos]=='\n') tmp[pos--]='\0'; if(r==0) in_progress=0; if(!strcmp(tmp, ".")) in_progress=0; } fprintf(wf, "239 %s\r\n", mid); } else { printf("<<< STUFF: %s", buf); } } printf("<<< Incoming connection %d closed.\n", pthread_self()); fclose(rf); fclose(wf); close(sock); } void *listener(void *arg) { struct sockaddr_in sin, sin_in; int s, sock, r, addrlen, on=1; struct in_cxn *new_cxn; sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = 5119; s=socket(AF_INET, SOCK_STREAM, 0); if(s<0) { perror("creating receive socket"); exit(2); } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); r=bind(s, (struct sockaddr *)&sin, sizeof(sin)); if(r<0) { perror("binding incoming port"); exit(2); } listen(s, 5); while((sock=accept(s, (struct sockaddr *)&sin_in, &addrlen)) >= 0) { pthread_t tid; pthread_create(&tid, NULL, &receiver, (void *)sock); } close(s); } int do_connect(struct sockaddr_in *sin) { char buf[BUFSIZE], tmp[BUFSIZE]; int s; s=socket(AF_INET, SOCK_STREAM, 0); if(s<0) { perror("creating send socket"); exit(2); } connect(s, (struct sockaddr *)sin, sizeof(struct sockaddr_in)); printf(">>> Connected to server\n"); read(s, buf, sizeof(buf)-1)[buf]='\0'; tmp[0]=buf[0]; tmp[1]=0; if(atoi(tmp) != 2) { printf(">>> Server error: %s", buf); exit(2); } return s; } void do_disconnect(int s) { char tmp[BUFSIZE]; snprintf(tmp, sizeof(tmp), "quit\r\n"); write(s, tmp, strlen(tmp)); close(s); printf(">>> Closed server connection.\n"); } void *sender(void *arg) { int r, s = (int)arg; char buf[BUFSIZE], tmp[BUFSIZE]; char hostname[BUFSIZE], msgid[BUFSIZE]; struct hist_ent *newhist; if(s<0) { printf(">>> Error: No active connection.\n"); return; } pthread_mutex_lock(&sender_mutex); gethostname(hostname, sizeof(hostname)); snprintf(msgid, sizeof(msgid), "", (long)time(NULL), hostname); snprintf(tmp, sizeof(tmp), "ihave %s\r\n", msgid); write(s, tmp, strlen(tmp)); read(s, buf, sizeof(buf)-1)[buf]='\0'; tmp[0]=buf[0]; tmp[1]=0; if(atoi(tmp) != 3) { printf(">>> Server already has article: %s", buf); } else { snprintf(tmp, sizeof(tmp), "Path: nntptime-test\r\n" "From: nntptime@test.foo\r\n" "Newsgroups: test.nntptime\r\n" "Subject: nntptime test\r\n" "Message-Id: %s\r\n" "\r\n" "test %s\r\n" ".\r\n", msgid, msgid); write(s, tmp, strlen(tmp)); } newhist=history_newent(); memcpy(newhist->msgid, msgid, strlen(msgid)+1); gettimeofday(&newhist->t_snd, NULL); pthread_mutex_lock(&history_mutex); newhist->next=history; history=newhist; pthread_mutex_unlock(&history_mutex); read(s, buf, sizeof(buf)-1)[buf]='\0'; tmp[0]=buf[0]; tmp[1]=0; if(atoi(tmp) != 2) printf(">>> Server rejected article: %s", buf); else printf(">>> Server accepted test article %s\n", msgid); pthread_mutex_unlock(&sender_mutex); } main(int argc, char *argv[]) { char *nntpserver; struct sockaddr_in sin; struct hostent *he; pthread_t tid_rcv, tid_snd; int s, running = 1; char cmd[BUFSIZE]; if(argc < 2) { printf("Usage: %s hostname [port]\n", argv[0]); exit(1); } else { sin.sin_family = AF_INET; nntpserver=argv[1]; if(argc > 2) sin.sin_port=htons(atoi(argv[2])); else sin.sin_port=htons(119); } he=gethostbyname(nntpserver); if(!he) { printf("%s: unknown host\n", nntpserver); } else { memcpy((caddr_t)&sin.sin_addr, he->h_addr_list[0], he->h_length); } history=NULL; pthread_mutex_init(&history_mutex, NULL); s=-1; pthread_mutex_init(&sender_mutex, NULL); pthread_create(&tid_rcv, NULL, &listener, NULL); while(running) { printf("--> "); fgets(cmd, sizeof(cmd), stdin); if(!strncasecmp(cmd, "connect", 7)) { pthread_mutex_lock(&sender_mutex); s=do_connect(&sin); pthread_mutex_unlock(&sender_mutex); } else if(!strncasecmp(cmd, "disconnect", 10)) { pthread_mutex_lock(&sender_mutex); do_disconnect(s); s=-1; pthread_mutex_unlock(&sender_mutex); } else if(!strncasecmp(cmd, "help", 4)) { printf("Commands: help connect disconnect send quit\n"); } else if(!strncasecmp(cmd, "send", 4)) { pthread_create(&tid_snd, NULL, &sender, (void *)s); } else if(!strncasecmp(cmd, "quit", 4)) { running=0; printf("Ciao.\n"); } else if(cmd[0]=='\r' || cmd[0]=='\n') { /* do nothing */ } else { printf("Unknown command -- try help.\n"); } } }