1 /* $OpenBSD: rfc5322.c,v 1.3 2021/06/14 17:58:16 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <errno.h> 20 #include <limits.h> 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "rfc5322.h" 25 26 struct buf { 27 char *buf; 28 size_t bufsz; 29 size_t buflen; 30 size_t bufmax; 31 }; 32 33 static int buf_alloc(struct buf *, size_t); 34 static int buf_grow(struct buf *, size_t); 35 static int buf_cat(struct buf *, const char *); 36 37 struct rfc5322_parser { 38 const char *line; 39 int state; /* last parser state */ 40 int next; /* parser needs data */ 41 int unfold; 42 const char *currhdr; 43 struct buf hdr; 44 struct buf val; 45 }; 46 47 struct rfc5322_parser * 48 rfc5322_parser_new(void) 49 { 50 struct rfc5322_parser *parser; 51 52 parser = calloc(1, sizeof(*parser)); 53 if (parser == NULL) 54 return NULL; 55 56 rfc5322_clear(parser); 57 parser->hdr.bufmax = 1024; 58 parser->val.bufmax = 65536; 59 60 return parser; 61 } 62 63 void 64 rfc5322_free(struct rfc5322_parser *parser) 65 { 66 free(parser->hdr.buf); 67 free(parser->val.buf); 68 free(parser); 69 } 70 71 void 72 rfc5322_clear(struct rfc5322_parser *parser) 73 { 74 parser->line = NULL; 75 parser->state = RFC5322_NONE; 76 parser->next = 0; 77 parser->hdr.buflen = 0; 78 parser->val.buflen = 0; 79 } 80 81 int 82 rfc5322_push(struct rfc5322_parser *parser, const char *line) 83 { 84 if (parser->line) { 85 errno = EALREADY; 86 return -1; 87 } 88 89 parser->line = line; 90 parser->next = 0; 91 92 return 0; 93 } 94 95 int 96 rfc5322_unfold_header(struct rfc5322_parser *parser) 97 { 98 if (parser->unfold) { 99 errno = EALREADY; 100 return -1; 101 } 102 103 if (parser->currhdr == NULL) { 104 errno = EOPNOTSUPP; 105 return -1; 106 } 107 108 if (buf_cat(&parser->val, parser->currhdr) == -1) 109 return -1; 110 111 parser->currhdr = NULL; 112 parser->unfold = 1; 113 114 return 0; 115 } 116 117 static int 118 _rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res) 119 { 120 size_t len; 121 const char *pos, *line; 122 123 line = parser->line; 124 125 switch(parser->state) { 126 127 case RFC5322_HEADER_START: 128 case RFC5322_HEADER_CONT: 129 res->hdr = parser->hdr.buf; 130 131 if (line && (line[0] == ' ' || line[0] == '\t')) { 132 parser->line = NULL; 133 parser->next = 1; 134 if (parser->unfold) { 135 if (buf_cat(&parser->val, "\n") == -1 || 136 buf_cat(&parser->val, line) == -1) 137 return -1; 138 } 139 res->value = line; 140 return RFC5322_HEADER_CONT; 141 } 142 143 if (parser->unfold) { 144 parser->val.buflen = 0; 145 parser->unfold = 0; 146 res->value = parser->val.buf; 147 } 148 return RFC5322_HEADER_END; 149 150 case RFC5322_NONE: 151 case RFC5322_HEADER_END: 152 if (line && (pos = strchr(line, ':'))) { 153 len = pos - line; 154 if (buf_grow(&parser->hdr, len + 1) == -1) 155 return -1; 156 (void)memcpy(parser->hdr.buf, line, len); 157 parser->hdr.buf[len] = '\0'; 158 parser->hdr.buflen = len + 1; 159 parser->line = NULL; 160 parser->next = 1; 161 parser->currhdr = pos + 1; 162 res->hdr = parser->hdr.buf; 163 res->value = pos + 1; 164 return RFC5322_HEADER_START; 165 } 166 167 return RFC5322_END_OF_HEADERS; 168 169 case RFC5322_END_OF_HEADERS: 170 if (line == NULL) 171 return RFC5322_END_OF_MESSAGE; 172 173 if (line[0] == '\0') { 174 parser->line = NULL; 175 parser->next = 1; 176 res->value = line; 177 return RFC5322_BODY_START; 178 } 179 180 errno = EINVAL; 181 return -1; 182 183 case RFC5322_BODY_START: 184 case RFC5322_BODY: 185 if (line == NULL) 186 return RFC5322_END_OF_MESSAGE; 187 188 parser->line = NULL; 189 parser->next = 1; 190 res->value = line; 191 return RFC5322_BODY; 192 193 case RFC5322_END_OF_MESSAGE: 194 errno = ENOMSG; 195 return -1; 196 197 default: 198 errno = EINVAL; 199 return -1; 200 } 201 } 202 203 int 204 rfc5322_next(struct rfc5322_parser *parser, struct rfc5322_result *res) 205 { 206 memset(res, 0, sizeof(*res)); 207 208 if (parser->next) 209 return RFC5322_NONE; 210 211 return (parser->state = _rfc5322_next(parser, res)); 212 } 213 214 static int 215 buf_alloc(struct buf *b, size_t need) 216 { 217 char *buf; 218 size_t alloc; 219 220 if (b->buf && b->bufsz >= need) 221 return 0; 222 223 if (need >= b->bufmax) { 224 errno = ERANGE; 225 return -1; 226 } 227 228 #define N 256 229 alloc = N * (need / N) + ((need % N) ? N : 0); 230 #undef N 231 buf = reallocarray(b->buf, alloc, 1); 232 if (buf == NULL) 233 return -1; 234 235 b->buf = buf; 236 b->bufsz = alloc; 237 238 return 0; 239 } 240 241 static int 242 buf_grow(struct buf *b, size_t sz) 243 { 244 if (SIZE_T_MAX - b->buflen <= sz) { 245 errno = ERANGE; 246 return -1; 247 } 248 249 return buf_alloc(b, b->buflen + sz); 250 } 251 252 static int 253 buf_cat(struct buf *b, const char *s) 254 { 255 size_t len = strlen(s); 256 257 if (buf_grow(b, len + 1) == -1) 258 return -1; 259 260 (void)memmove(b->buf + b->buflen, s, len + 1); 261 b->buflen += len; 262 return 0; 263 } 264