#include "main.h" /* * net.c Copyright 1999 Christopher M Sedore. All Rights Reserved. * Please see the "COPYING" file for license information. * * This file is something of a mess, unfortunately. The code below * is essentially responsible for io on sockets. It provides 3 * main functions: 1) it provides upcalls with each line of input * when not reading an article. 2) it will read an entire article * then do an upcall. 3) it will copy an article from a store to * a socket. * * All these functions are async--there is no blocking anywhere in * the upcall functions. Introducing any blocking system calls * would be detrimental to performance. */ void ReadComplete(struct context *cc, int len); /* * NextIo() * * NextIo() is the routine normally called when another async operation * (read or write) should be queued on a socket. It also will close * the socket now, or after the next operation completes. */ void NextIo(struct context *cc) { if (cc->flags & NNTP_CLOSE_CONNECTION) { if (cc->incoming) { cc->incoming->connected--; TAILQ_REMOVE(&cc->incoming->activeConns,cc,clist); } printf("closing\n"); if (cc->errorCallback) cc->errorCallback(cc); if (cc->flags & NNTP_READ_ARTICLE) CleanupAfterReadArticle(cc->art); close(cc->cb.cb.aio_fildes); free(cc); return; } if (cc->obuflen) { cc->cb.state="nio-wr"; WriteConn(cc); } else { cc->cb.state="nio-rd"; if (cc->flags & NNTP_READ_ARTICLE) ReadConnArt(cc); else ReadConn(cc); } if (cc->flags & NNTP_CLOSE_CONNECTION_AFTER_OP) { cc->flags|=NNTP_CLOSE_CONNECTION; } return; } /* * ReadConnArt() * * ReadConnArt() is a special case function. It handles reading from a * socket when we are reading an article. Special handling is needed * because the buffering is different. */ int ReadConnArt(struct context *cc) { cc->cb.cb.aio_buf=&cc->art->buf[cc->art->len]; cc->cb.cb.aio_nbytes= cc->art->bufsz-(cc->art->len+255)>CONTEXT_BUF_SIZE ? CONTEXT_BUF_SIZE-16 : cc->art->bufsz-(cc->art->len+255); assert(cc->cb.cb.aio_nbytes>0); cc->flags|=NNTP_ASYNC_ACT; cc->cb.state="rc-art"; cc->cb.callback=(void(*)(void *,int))ReadComplete; cc->cb.cb._aiocb_private.privatemodes=0; if (aio_read((struct aiocb *)cc)) { perror("ReadComplete (art)"); } } /* * ReadComplete() * * ReadComplete() is the low level upcall that runs after a aio_read call * completes. This is where it gets ugly :(. This code handles two * cases: 1) we're reading an article. 2) we're reading commands * (line at a time). */ void ReadComplete(struct context *cc, int len) { int noNewOp=0; char *s,*e; int r; cc->flags&=~NNTP_ASYNC_ACT; assert(cc->callback!=NULL); if ((cc->cb.logfd) && (len>0)) { write(cc->cb.logfd,(char *)cc->cb.cb.aio_buf,len); } if (cc->flags & NNTP_FEED_START) { if (len>-1) { cc->buflen+=len; cc->cb.state="rd-cb"; cc->callback(cc); } else { cc->flags|=NNTP_CLOSE_CONNECTION; perror("rc/fs"); } NextIo(cc); return; } if (len<0) { printf("connclose/rc (%i) (%s/%s)\r\n",len,cc->state,cc->cb.state); perror("cc/rc"); cc->flags|=NNTP_CLOSE_CONNECTION; cc->connErrorCode=errno; NextIo(cc); return; } /* * A zero length read should mean a remote connection closed */ if (len==0) { cc->flags|=NNTP_CLOSE_CONNECTION; cc->connErrorCode=0; NextIo(cc); return; } if (len==0x7FFFFFFF) len=0; // XXX This is how we do it recursively with no chars while (cc->flags & NNTP_READ_ARTICLE) { s=&cc->art->buf[cc->art->len]; if (cc->art->len>4) { s-=4; } else { s=cc->art->buf; } cc->art->len+=len; /* * is the article empty? */ if ((cc->art->buf[0]=='.') && (cc->art->buf[1]=='\r') && (cc->art->buf[2]=='\n')) { int l; l=cc->art->len-3; cc->art->len=0; cc->cb.state="rda-cb"; if (l) { cc->buflen=l; memcpy(cc->buf,&cc->art->buf[3],l); } else { cc->buflen=0; } if (cc->callback(cc)) { noNewOp=1; } else { noNewOp=0; } cc->bp=cc->buf; len=0; continue; } e=NULL; /* * is the article complete? */ while (s-cc->art->bufart->len-4) { if (((s[0]=='\r') && (s[1]=='\n') && (s[2]=='.') && (s[3]=='\r') && (s[4]=='\n'))) { e=s; break; } s++; } /* * If the article is not complete, we need to make sure * that our buffer hasn't overrun. Note that the 2 level * extent-like allocator is a target for replacement. * If we haven't got the whole artice, read again. */ if (!e) { if (cc->art->len>=cc->art->bufsz-265) { if (cc->art->bufsz==BASE_ARTSIZE) { cc->art->buf= realloc(cc->art->bufp,MAX_ARTSIZE); assert(cc->art->buf!=NULL); cc->art->bufsz=MAX_ARTSIZE; cc->art->bufp=cc->art->buf; cc->art->buf+=255; } else { cc->flags|=NNTP_BAD_ARTICLE; printf("oversize\n"); memcpy(cc->art->buf, &cc->art->buf[cc->art->len-5],5); cc->art->len=5; } } ReadConnArt(cc); return; } /* * The whole article has been received. Mess with the * buffer, upcall, and then copy any remaining data * back in to the normal connection buffer. */ if (e) { int l; l=cc->art->len-((s+5)-cc->art->buf); cc->art->len=s-cc->art->buf; // *s=0; if (l) { cc->buflen=l; assert(l>-1); assert(lbufsz); memcpy(cc->buf,s+5,l); } else { cc->buflen=0; } cc->cb.state="rda-cb"; if (cc->callback(cc)) { noNewOp=1; } else { noNewOp=0; } cc->bp=cc->buf; len=0; } } /* * We get here when not reading an article. The code below * takes data read from a socket, finds the \r\n, replaces \r * with a NUL, then upcalls to process the command. */ cc->buflen+=len; if (noNewOp) return; if (!cc->buflen) { NextIo(cc); return; } while (1) { s=cc->bp; while ((*s!='\r') && (s-cc->bufbuflen)) s++; if (s-cc->buf==cc->bufsz) { if (cc->bp>cc->buf) { cc->buflen-=cc->bp-cc->buf; memcpy(cc->buf,cc->bp,cc->buflen); cc->bp=cc->buf; NextIo(cc); cc->cb.state="rd-ovf"; return; } else { cc->flags|=NNTP_CLOSE_CONNECTION; printf("overflow"); NextIo(cc); return; } } if ((s-cc->buf==cc->buflen) || (*s!='\r')) { NextIo(cc); return; } *s=0; cc->cb.state="rd-cb"; r=cc->callback(cc); s+=2; cc->bp=s; if (cc->buflenbp-cc->buf) cc->bp=&cc->buf[cc->buflen]; if (cc->buflen>cc->bufsz/2) cc->flags|=NNTP_BUF_COMPRESS; if (cc->flags & NNTP_BUF_COMPRESS) { if (cc->bp>cc->buf) { cc->buflen-=cc->bp-cc->buf; memcpy(cc->buf,cc->bp,cc->buflen); } else { printf("why are we here %s %u\n", __FILE__,__LINE__); cc->buflen=0; } cc->bp=cc->buf; cc->flags&=~NNTP_BUF_COMPRESS; } if (cc->bp==&cc->buf[cc->buflen]) { cc->bp=cc->buf; cc->buflen=0; } /* * if the return from the upcall was nonzero, then * we don't initiate another async operation. */ if (r) return; if (cc->flags & NNTP_READ_ARTICLE) { cc->buflen-=cc->bp-cc->buf; assert(cc->buflen>-1); assert((cc->bp>=cc->buf) && (cc->bpbuf+cc->bufsz)); cc->art->len=0; if (cc->buflen) { memcpy(cc->art->buf,cc->bp,cc->buflen); ReadComplete(cc,cc->buflen); } else { NextIo(cc); } return; } } } /* * WriteComplete() * * WriteComplete() is a low level upcall executed upon the completion of * an aio_write operation. It doesn't do much since we driven exclusively * by reads. */ void WriteComplete(struct context *cc,int len) { cc->flags&=~NNTP_ASYNC_ACT; if (len<1) { printf("connclose/wc (%i) (%s/%s) (%u %s)\r\n",len,cc->state,cc->cb.state,cc->obuflen,cc->obuf); cc->flags|=NNTP_CLOSE_CONNECTION; cc->connErrorCode=errno; NextIo(cc); return; } cc->obuflen-=len; assert(cc->callback!=NULL); // if (cc->callback(cc)) // return; NextIo(cc); } /* * WriteConn() * * WriteConn is called to send the contents of cc->obuf. Writes take * precedence over reads, so if there are bytes to be sent, NextIo() * will call WriteConn. */ int WriteConn(struct context *cc) { assert(!(cc->flags & NNTP_ASYNC_ACT)); assert(cc->obuflen<10000000); cc->flags|=NNTP_ASYNC_ACT; cc->cb.cb.aio_buf=cc->obuf; cc->cb.cb.aio_nbytes=cc->obuflen; cc->cb.callback=(void(*)(void *,int))WriteComplete; cc->cb.cb._aiocb_private.privatemodes=0; if (cc->cb.logfd) { // write(1,cc->obuf,cc->obuflen); write(cc->cb.logfd,cc->obuf,cc->obuflen); } if (aio_write((struct aiocb *)cc)) perror("WriteConn"); } /* * ReadConn() * * ReadConn reads from the socket into the command buffer. */ int ReadConn(struct context *cc) { assert(!(cc->flags & NNTP_ASYNC_ACT)); cc->flags|=NNTP_ASYNC_ACT; cc->cb.cb.aio_buf=&cc->buf[cc->buflen]; cc->cb.cb.aio_nbytes=cc->bufsz-cc->buflen; cc->cb.callback=(void(*)(void *,int))ReadComplete; cc->cb.cb._aiocb_private.privatemodes=0; if (aio_read((struct aiocb *)cc)) perror("ReadConn"); } /* * CopyArtDone() * CopyArtSendArt() * CopyArt() * * These three functions work together to async send an article. * CopyArt() sets up the context aiocb to read an article and * asks the store functions to read it. CopyArtSendArt is the * callback routine for CopyArt, and takes the (just read) article * and aio_write's it to the wire. CopyArtDone is the callback * for CopyArtSendArt, and makes sure the article went, then does * cleanup and resumes normal processing. */ void CopyArtDone(struct context *cc, int len) { cc->cb.cb.aio_nbytes-=len; //printf("sent=%i,len=%i,left=%i\n",cc->obuflen,len,cc->cb.cb.aio_nbytes); if (cc->cb.cb.aio_nbytes) { printf("cad-connclose left=%i sent=%i\r\n",cc->cb.cb.aio_nbytes,len); perror("cad"); cc->connErrorCode=errno; cc->flags|=NNTP_CLOSE_CONNECTION; free(cc->hold); NextIo(cc); return; } /* * This block should have only been necessary when I forgot to turn off * FIONBIO on the socket */ /* if (cc->cb.cb.aio_nbytes) { cc->obuflen+=len; cc->cb.cb.aio_buf=cc->hold+cc->obuflen; if (aio_write((struct aiocb *)cc)) perror("aio_write in CopyArtDone"); return; } */ cc->flags&=~NNTP_ASYNC_ACT; cc->cb.cb.aio_buf-=cc->obuflen; assert(cc->cb.cb.aio_buf==cc->hold); cc->obuflen=0; free((void *)cc->cb.cb.aio_buf); cc->cb.cb.aio_buf=&cc->buf[cc->buflen]; cc->cb.cb.aio_nbytes=cc->bufsz-cc->buflen; assert(cc->callback!=NULL); if (cc->buflen) { // printf("copy done,rc\n"); ReadComplete(cc,0x7fffffff); } else { // printf("copy done,cc\n"); if (!cc->callback(cc)) { // printf("nio o=%i\n",cc->obuflen); // printf("<=%s\n<=%s\n",cc->buf,cc->bp); NextIo(cc); } } } void CopyArtSendArt(struct context *cc,int len) { int store; u_quad_t offset; assert(!(cc->flags & NNTP_ASYNC_ACT)); cc->flags|=NNTP_ASYNC_ACT; if (len<0) { abort(); } if (cc->obuflen) { cc->cb.cb.aio_buf-=cc->obuflen; memcpy((void *)cc->cb.cb.aio_buf,cc->obuf,cc->obuflen); cc->cb.cb.aio_nbytes+=cc->obuflen; cc->obuflen=0; } if (DecipherKey(cc->key,&store,&offset)) { printf("Missing after READ!?!\n"); cc->cb.cb.aio_nbytes=cc->obuflen; } ((char *)cc->cb.cb.aio_buf)[cc->cb.cb.aio_nbytes++]='\r'; ((char *)cc->cb.cb.aio_buf)[cc->cb.cb.aio_nbytes++]='\n'; ((char *)cc->cb.cb.aio_buf)[cc->cb.cb.aio_nbytes++]='.'; ((char *)cc->cb.cb.aio_buf)[cc->cb.cb.aio_nbytes++]='\r'; ((char *)cc->cb.cb.aio_buf)[cc->cb.cb.aio_nbytes++]='\n'; cc->cb.cb.aio_fildes=cc->fd; cc->cb.callback=(void(*)(void *,int))CopyArtDone; cc->cb.state="sndart"; if (aio_write((struct aiocb *)cc)) { cc->flags|=NNTP_CLOSE_CONNECTION; free(cc->hold); printf("close on send\n"); NextIo(cc); } } void MapAndSendCleanup(struct context *cc, int len) { cc->flags&=~NNTP_ASYNC_ACT; assert(len==cc->artlen); UnMapStore((void *)cc->cb.cb.aio_buf,cc->artlen); cc->cb.cb.aio_buf=&cc->buf[cc->buflen]; cc->cb.cb.aio_nbytes=cc->bufsz-cc->buflen; cc->obuf[cc->obuflen++]='\r'; cc->obuf[cc->obuflen++]='\n'; cc->obuf[cc->obuflen++]='.'; cc->obuf[cc->obuflen++]='\r'; cc->obuf[cc->obuflen++]='\n'; NextIo(cc); } void MapAndSendArt(struct context *cc, int len) { cc->flags&=~NNTP_ASYNC_ACT; if (len<0) { printf("connclose/masa (%i) (%s/%s) (%u %s)\r\n",len,cc->state,cc->cb.state,cc->obuflen,cc->obuf); cc->flags|=NNTP_CLOSE_CONNECTION; NextIo(cc); return; } if (len!=cc->obuflen) { printf("lenghts not equal? %u!=%u\n",len,cc->obuflen); } // assert(len==cc->obuflen); cc->obuflen-=len; cc->hold=MapFromStore(cc->key,cc->artlen); if (cc->hold==NULL) { printf("no mmap\n"); cc->obuf[cc->obuflen++]='.'; cc->obuf[cc->obuflen++]='\r'; cc->obuf[cc->obuflen++]='\n'; NextIo(cc); return; } cc->cb.cb.aio_buf=cc->hold; cc->cb.cb.aio_nbytes=cc->artlen; cc->cb.callback=(void(*)(void *,int))MapAndSendCleanup; cc->cb.state="mapsnd"; cc->flags|=NNTP_ASYNC_ACT; if (aio_write((struct aiocb *)cc)) { cc->flags|=NNTP_CLOSE_CONNECTION; UnMapStore(cc->hold,cc->artlen); printf("close on send\n"); NextIo(cc); } } int CopyArt(struct context *cc,struct artent *pae) { #ifndef USEMMAPSENDS if (pae->magic!=ARTENTRY_MAGIC) return -1; assert((pae->artlen+16+cc->obuflen)hold=(void *)cc->cb.cb.aio_buf=malloc(pae->artlen+16+cc->obuflen); assert(cc->hold!=NULL); cc->cb.cb.aio_buf+=cc->obuflen; cc->cb.cb.aio_nbytes=pae->artlen; cc->cb.callback=(void(*)(void *,int))CopyArtSendArt; cc->cb.state="cpart"; cc->key=pae->key; if (GetFromStore(pae->key,(struct myaiocb *)cc)) { free(cc->hold); return -1; } #else cc->key=pae->key; cc->artlen=pae->artlen; if (cc->obuflen) { WriteConn(cc); cc->cb.callback=MapAndSendArt; return 0; } MapAndSendArt(cc,0); return 0; #endif }