1 /* David Leonard, 2002. Public domain. */
2 /* $Id: tcp_http.c 1193 2007-08-30 10:33:24Z d $ */
3 
4 #if HAVE_CONFIG_H
5 # include "config.h"
6 #endif
7 
8 #if STDC_HEADERS
9 # include <stdio.h>
10 # include <string.h>
11 # include <stdlib.h>
12 # include <ctype.h>
13 #endif
14 #if HAVE_SYS_TYPES_H
15 # include <sys/types.h>
16 #endif
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27 
28 #include "type.h"
29 #include "flow.h"
30 #include "tcp.h"
31 #include "compat.h"
32 
33 #define CR  '\r'
34 #define LF  '\n'
35 
36 struct smtp_state {
37 	int in_data;		    /* true while client sends DATA */
38 	int in_header;		    /* true in DATA's headers */
39 	char line[2048];	    /* buffer for data sent to server */
40 	int line_len;		    /* length of line read so far */
41 	enum { sLINE, sEXPECT_LF, sENCRYPTED } state;
42 	char to_addr[512];	    /* RCPT TO: */
43 	char from_addr[512];	    /* MAIL FROM: */
44 };
45 
46 /* Returns pointer to string after prefix, or NULL if no match */
47 const char *
strip_prefix(s,prefix)48 strip_prefix(s, prefix)
49 	const char *s;
50 	const char *prefix;
51 {
52 	while (*prefix)
53 	    if (*s++ != *prefix++)
54 		return 0;
55 	return s;
56 }
57 
58 /* Normalize a line by uppercasing the command word(s), and stripping
59  * and collapsing whitespace */
60 static void
normalize_line(line)61 normalize_line(line)
62 	char *line;
63 {
64 	char *s, *p;
65 	int has_colon;
66 
67 	s = p = line;
68 	/* Skip leading whitespace */
69 	while (*s == ' ' || *s == '\t')
70 	    s++;
71 	/* Collapse whitespace */
72 	while (*s && *s != ':') {
73 	    if (*s == ' ' || *s == '\t') {
74 		while (*s == ' ' || *s == '\t')
75 		    s++;
76 		*p++ = ' ';
77 	    } else {
78 		*p++ = *s++;
79 	    }
80 	}
81 	/* Remove whitespace before a colon */
82 	if (*s == ':' && p > line && p[-1] == ' ')
83 	    p--;
84 	if (*s == ':') {
85 	    *p++ = *s++;
86 	    has_colon = 1;
87 	} else
88 	    has_colon = 0;
89 	/* Remove whitespace after colon */
90 	while (*s == ' ' || *s == '\t')
91 	    s++;
92 	/* Copy the rest of the line, collapsing whitespace */
93 	while (*s) {
94 	    if (*s == ' ' || *s == '\t') {
95 		while (*s == ' ' || *s == '\t')
96 		    s++;
97 		*p++ = ' ';
98 	    } else
99 		*p++ = *s++;
100 	}
101 	/* Strip trailing whitespace */
102 	if (p > line && p[-1] == ' ')
103 	    p--;
104 	*p = 0;
105 
106 	/* Uppercase the first word, unless there is a colon in which case
107 	 * we uppercase to the colon */
108 	for (s = line; *s && *s != (has_colon ? ':' : ' '); s++)
109 	    if (*s != ' ')
110 		*s = toupper(*s);
111 }
112 
113 /* Normalize an email address
114  *   u@h    -> u@h
115  *   N<u@h> -> u@h
116  */
117 
118 static void
normalize_addr(line)119 normalize_addr(line)
120 	char *line;
121 {
122 	char *s, *t, *u;
123 
124 	/* Search for the opening angle bracket */
125 	for (s = line; *s; s++)
126 	    if (*s == '<')
127 		break;
128 	if (!*s)
129 	    return; /* no '<' */
130 	s++;	    /* skip '<' */
131 
132 	/* Check that there is a closing bracket */
133 	for (u = s; *u; u++)
134 	    if (*u == '>')
135 		break;
136 	if (*u != '>')
137 	    return; /* no '>' */
138 
139 	for (t = line; s < u; t++,s++)
140 	    *t = *s;
141 	*t = 0;
142 }
143 
144 static void
smtp_line(f,line)145 smtp_line(f, line)
146 	struct flow *f;
147 	const char *line;
148 {
149 	struct smtp_state *state;
150 	const char *s;
151 	int addr_changed = 0;
152 FILE*log;
153 
154 	state = (struct smtp_state *)f->udata;
155 
156 if ((log = fopen("/tmp/smtp.log", "a")))
157    fprintf(log, "smpt_line [%s]\n", line);
158 
159 	if (state->in_data) {
160 	    if (strcmp(line, ".") == 0) {
161 		state->in_data = 0;
162 		state->from_addr[0] = 0;
163 		state->to_addr[0] = 0;
164 	    } else if (state->in_header) {
165 		if (!*line)
166 		    state->in_header = 0;
167 #if 0
168 		/* Tag the Subject: line inside DATA */
169 		else if ((line[0] == 'S' || line[0] == 's') &&
170 		         (line[1] == 'U' || line[1] == 'u') &&
171 		         (line[2] == 'B' || line[2] == 'b') &&
172 		         (line[3] == 'J' || line[3] == 'j') &&
173 		         (line[4] == 'E' || line[4] == 'e') &&
174 		         (line[5] == 'C' || line[5] == 'c') &&
175 		         (line[6] == 'T' || line[6] == 't') &&
176 		         line[7] == ':')
177 		    snprintf(f->desc, sizeof f->desc, "%s", line);
178 #endif
179 	    }
180 	} else {
181 	    /* Normalize the command line */
182 	    normalize_line(line);
183 if (log)fprintf(log, "normalized to [%s]\n", line);
184 	    if ((s = strip_prefix(line, "MAIL FROM:"))) {
185 		snprintf(state->from_addr, sizeof state->from_addr, "%s", s);
186 		normalize_addr(state->from_addr);
187 		addr_changed = 1;
188 if (log)fprintf(log, "from_addr = [%s]\n", state->from_addr);
189 	    }
190 	    else if ((s = strip_prefix(line, "RCPT TO:"))) {
191 		snprintf(state->to_addr, sizeof state->to_addr, "%s", s);
192 		normalize_addr(state->to_addr);
193 		addr_changed = 1;
194 if (log)fprintf(log, "to_addr = [%s]\n", state->to_addr);
195 	    }
196 	    else if (strcmp(line, "DATA") == 0) {
197 		state->in_data = 1;
198 		state->in_header = 1;
199 	    }
200 	    else if (strcmp(line, "STARTTLS") == 0) {
201 		state->state = sENCRYPTED;
202 		snprintf(f->desc, sizeof f->desc, "STARTTLS");
203 	    }
204 
205 	    if ((addr_changed ||
206 	         strcmp(line, "QUIT") == 0 ||
207 		 strcmp(line, "DATA") == 0) &&
208 		(*state->from_addr && *state->to_addr))
209 	    {
210 		snprintf(f->desc, sizeof f->desc, "%s -> %s",
211 		    state->from_addr, state->to_addr);
212 	    } else {
213 		if (strcmp(line, "QUIT") != 0)
214 		    snprintf(f->desc, sizeof f->desc, "%s", line);
215 	    }
216 if (log)fprintf(log, "set desc to: [%s]\n", f->desc);
217 	}
218 
219 if (log)fclose(log);
220 }
221 
222 /*
223  * Look for simple SMTP (RFC 2822) commands.
224  */
225 void
tcp_smtp(f,data,end,toserver)226 tcp_smtp(f, data, end, toserver)
227 	struct flow *f;
228 	const char *data;
229 	const char *end;
230 	int toserver;
231 {
232 	const char *d;
233 	struct smtp_state *state;
234 
235 	if (!toserver)
236 	    return;
237 
238 	if (!f->udata) {
239 	    /* Initialize state */
240 	    state = (struct smtp_state *)malloc(sizeof *state);
241 	    if (!state)
242 		errx(1, "malloc");
243 	    memset(state, 0, sizeof *state);
244 	    f->udata = state;
245 	    f->freeudata = free;
246 	    state->line_len = 0;
247 	    state->state = sLINE;
248 	    state->to_addr[0] = 0;
249 	    state->from_addr[0] = 0;
250 	} else
251 	    state = (struct smtp_state *)f->udata;
252 
253 	/* Extract data, line by line */
254 	for (d = data; d < end; d++)
255 	    switch (state->state) {
256             case sENCRYPTED:
257                 /* Don't bother */
258                 d = end - 1;
259                 break;
260 
261 	    case sLINE:
262 		if (*d == CR) {
263 		    state->state = sEXPECT_LF;
264 		} else {
265 		    if (state->line_len < sizeof state->line - 1)
266 			state->line[state->line_len++] = *d;
267 		    /*state->state = sLINE;*/
268 		}
269 		break;
270 	    case sEXPECT_LF:
271 		if (*d == LF) {
272 		    state->line[state->line_len] = 0;
273 		    smtp_line(f, state->line);
274 		    state->line_len = 0;
275 		    state->state = sLINE;
276 		} else if (*d == CR) {
277 		    state->line_len = 0;
278 		    /*state->state = sEXPECT_LF;*/
279 		} else {
280 		    state->line[0] = *d;
281 		    state->line_len = 1;
282 		    state->state = sLINE;
283 		}
284 		break;
285 	    }
286 }
287