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