warp-vpn

point to point VPN implementation
git clone git://git.2f30.org/warp-vpn
Log | Files | Refs | README

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 }