netpkt.c (3794B)
1 #include <errno.h> 2 #include <stdint.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 6 #if defined(__linux__) 7 #include <bsd/stdlib.h> 8 #endif 9 10 #include "warp.h" 11 12 /* the various states the input handler can be in */ 13 enum { 14 RXINITIAL, 15 RXNONCE, 16 RXHDR, 17 RXPAYLOAD, 18 RXTAG, 19 RXDECRYPT, 20 RXDISCARD 21 }; 22 23 /* output buffer for writing data to the network */ 24 static unsigned char *wbuf; 25 /* input buffer for reading data from the network */ 26 static unsigned char *rbuf; 27 28 /* total number of bytes read into input buffer */ 29 static size_t rbuftotal; 30 /* number of bytes remaining to read into buffer for current state */ 31 static size_t rbufrem; 32 33 /* maximum input/output buffer size */ 34 static size_t maxbuflen; 35 36 /* nonce size in bytes */ 37 static size_t noncelen; 38 /* tag size in bytes */ 39 static size_t taglen; 40 41 /* state tracking for input handling */ 42 static int rxstate; 43 44 int 45 netwrite(int fd, unsigned char *pt, unsigned long long ptlen, 46 unsigned long long *outlen) 47 { 48 unsigned char *p = wbuf; 49 unsigned long long buflen = noncelen + HDRLEN + ptlen + taglen; 50 int n, total = 0; 51 52 if (buflen > maxbuflen) 53 fatalx("packet is too large"); 54 55 arc4random_buf(wbuf, noncelen); 56 pack16(&wbuf[noncelen], ptlen); 57 if (cryptoseal(&wbuf[noncelen + HDRLEN], outlen, 58 pt, ptlen, &wbuf[noncelen], HDRLEN, wbuf) != 0) { 59 logwarnx("cryptoseal failed"); 60 return -1; 61 } 62 63 /* spin until all data is written */ 64 while (buflen > 0) { 65 n = write(fd, p + total, buflen); 66 if (n == 0) { 67 return PKTFAILED; 68 } else if (n < 0) { 69 if (errno == EWOULDBLOCK) 70 continue; 71 return PKTFAILED; 72 } 73 total += n; 74 buflen -= n; 75 } 76 return PKTCOMPLETE; 77 } 78 79 int 80 netread(int fd, unsigned char *pt, unsigned long long ptlen, 81 unsigned long long *outlen) 82 { 83 unsigned long long buflen = noncelen + HDRLEN + ptlen + taglen; 84 unsigned long long ctlen; 85 int n; 86 87 if (buflen > maxbuflen) 88 fatalx("packet is too large"); 89 90 for (;;) { 91 switch (rxstate) { 92 case RXINITIAL: 93 rbuftotal = 0; 94 rbufrem = noncelen; 95 rxstate = RXNONCE; 96 break; 97 case RXNONCE: 98 if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0) 99 goto out; 100 rbuftotal += n; 101 rbufrem -= n; 102 if (rbufrem == 0) { 103 rbufrem = HDRLEN; 104 rxstate = RXHDR; 105 } 106 break; 107 case RXHDR: 108 if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0) 109 goto out; 110 rbuftotal += n; 111 rbufrem -= n; 112 if (rbufrem == 0) { 113 ctlen = unpack16(&rbuf[noncelen]); 114 if (ctlen > ptlen) { 115 rxstate = RXDISCARD; 116 } else { 117 rbufrem = ctlen; 118 rxstate = RXPAYLOAD; 119 } 120 } 121 break; 122 case RXPAYLOAD: 123 if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0) 124 goto out; 125 rbuftotal += n; 126 rbufrem -= n; 127 if (rbufrem == 0) { 128 rbufrem = taglen; 129 rxstate = RXTAG; 130 } 131 break; 132 case RXTAG: 133 if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0) 134 goto out; 135 rbuftotal += n; 136 rbufrem -= n; 137 if (rbufrem == 0) 138 rxstate = RXDECRYPT; 139 break; 140 case RXDECRYPT: 141 rxstate = RXINITIAL; 142 if (cryptoopen(pt, outlen, &rbuf[noncelen + HDRLEN], 143 rbuftotal - noncelen - HDRLEN, 144 &rbuf[noncelen], HDRLEN, rbuf) != 0) { 145 logwarnx("cryptoopen failed"); 146 return PKTPARTIAL; 147 } 148 return PKTCOMPLETE; 149 case RXDISCARD: 150 rxstate = RXINITIAL; 151 for (;;) 152 if ((n = read(fd, rbuf, maxbuflen)) <= 0) 153 goto out; 154 break; 155 } 156 } 157 out: 158 if (n < 0 && errno == EWOULDBLOCK) 159 return PKTPARTIAL; 160 return PKTFAILED; 161 } 162 163 /* Reset state machine. This is required when a fatal error occurs. */ 164 void 165 netreset(void) 166 { 167 rxstate = RXINITIAL; 168 } 169 170 void 171 netinit(void) 172 { 173 noncelen = cryptononcelen(); 174 taglen = cryptotaglen(); 175 maxbuflen = noncelen + HDRLEN + MAXPAYLOADLEN + taglen; 176 if (!(wbuf = malloc(maxbuflen))) 177 fatal("malloc"); 178 if (!(rbuf = malloc(maxbuflen))) 179 fatal("malloc"); 180 netreset(); 181 }