stun

simple point to point tunnel
git clone git@git.2f30.org/stun.git
Log | Files | Refs | README

netpkt.c (3828B)


      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 		logwarnx("packet is too large");
     54 		return PKTFAILED;
     55 	}
     56 
     57 	arc4random_buf(wbuf, noncelen);
     58 	pack16(&wbuf[noncelen], ptlen);
     59 	if (cryptoseal(&wbuf[noncelen + HDRLEN], outlen,
     60 	               pt, ptlen, &wbuf[noncelen], HDRLEN, wbuf) != 0) {
     61 		logwarnx("cryptoseal failed");
     62 		return -1;
     63 	}
     64 
     65 	/* spin until all data is written */
     66 	while (buflen > 0) {
     67 		n = write(fd, p + total, buflen);
     68 		if (n == 0) {
     69 			return PKTFAILED;
     70 		} else if (n < 0) {
     71 			if (errno == EWOULDBLOCK)
     72 				continue;
     73 			return PKTFAILED;
     74 		}
     75 		total += n;
     76 		buflen -= n;
     77 	}
     78 	return PKTCOMPLETE;
     79 }
     80 
     81 int
     82 netread(int fd, unsigned char *pt, unsigned long long ptlen,
     83         unsigned long long *outlen)
     84 {
     85 	unsigned long long buflen = noncelen + HDRLEN + ptlen + taglen;
     86 	int n, ctlen;
     87 
     88 	if (buflen > maxbuflen) {
     89 		logwarnx("packet is too large");
     90 		return PKTFAILED;
     91 	}
     92 
     93 	for (;;) {
     94 		switch (rxstate) {
     95 		case RXINITIAL:
     96 			rbuftotal = 0;
     97 			rbufrem = noncelen;
     98 			rxstate = RXNONCE;
     99 			break;
    100 		case RXNONCE:
    101 			if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0)
    102 				goto out;
    103 			rbuftotal += n;
    104 			rbufrem -= n;
    105 			if (rbufrem == 0) {
    106 				rbufrem = HDRLEN;
    107 				rxstate = RXHDR;
    108 			}
    109 			break;
    110 		case RXHDR:
    111 			if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0)
    112 				goto out;
    113 			rbuftotal += n;
    114 			rbufrem -= n;
    115 			if (rbufrem == 0) {
    116 				ctlen = unpack16(&rbuf[noncelen]);
    117 				if (ctlen > ptlen) {
    118 					rxstate = RXDISCARD;
    119 				} else {
    120 					rbufrem = ctlen;
    121 					rxstate = RXPAYLOAD;
    122 				}
    123 			}
    124 			break;
    125 		case RXPAYLOAD:
    126 			if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0)
    127 				goto out;
    128 			rbuftotal += n;
    129 			rbufrem -= n;
    130 			if (rbufrem == 0) {
    131 				rbufrem = taglen;
    132 				rxstate = RXTAG;
    133 			}
    134 			break;
    135 		case RXTAG:
    136 			if ((n = read(fd, rbuf + rbuftotal, rbufrem)) <= 0)
    137 				goto out;
    138 			rbuftotal += n;
    139 			rbufrem -= n;
    140 			if (rbufrem == 0)
    141 				rxstate = RXDECRYPT;
    142 			break;
    143 		case RXDECRYPT:
    144 			rxstate = RXINITIAL;
    145 			if (cryptoopen(pt, outlen, &rbuf[noncelen + HDRLEN],
    146 			               rbuftotal - noncelen - HDRLEN,
    147 			               &rbuf[noncelen], HDRLEN, rbuf) != 0) {
    148 				logwarnx("cryptoopen failed");
    149 				return PKTPARTIAL;
    150 			}
    151 			return PKTCOMPLETE;
    152 		case RXDISCARD:
    153 			rxstate = RXINITIAL;
    154 			for (;;)
    155 				if ((n = read(fd, rbuf, maxbuflen)) <= 0)
    156 					goto out;
    157 			break;
    158 		}
    159 	}
    160 out:
    161 	if (n < 0 && errno == EWOULDBLOCK)
    162 		return PKTPARTIAL;
    163 	return PKTFAILED;
    164 }
    165 
    166 /* Reset state machine.  This is required when a fatal error occurs. */
    167 void
    168 netreset(void)
    169 {
    170 	rxstate = RXINITIAL;
    171 }
    172 
    173 void
    174 netinit(void)
    175 {
    176 	noncelen = cryptononcelen();
    177 	taglen = cryptotaglen();
    178 	maxbuflen = noncelen + HDRLEN + MAXPAYLOADLEN + taglen;
    179 	if (!(wbuf = malloc(maxbuflen)))
    180 		fatal("malloc");
    181 	if (!(rbuf = malloc(maxbuflen)))
    182 		fatal("malloc");
    183 	netreset();
    184 }