Line data Source code
1 31 : int mail_route(LOGGER log, MAIL* mail, DATABASE* database){
2 : unsigned i;
3 31 : unsigned rv = 250;
4 : struct timespec time_spec;
5 :
6 : //generate a message id
7 31 : if(clock_gettime(CLOCK_REALTIME_COARSE, &time_spec) < 0){
8 0 : logprintf(log, LOG_ERROR, "Failed to get time for message id generation: %s\n", strerror(errno));
9 0 : time_spec.tv_sec = time(NULL);
10 : }
11 :
12 31 : snprintf(mail->message_id, CMAIL_MESSAGEID_MAX, "%X%X.%X-%s", (unsigned)time_spec.tv_sec, (unsigned)time_spec.tv_nsec, rand(), mail->submitter);
13 31 : logprintf(log, LOG_INFO, "Generated message ID %s\n", mail->message_id);
14 :
15 : //iterate over recipients
16 58 : for(i = 0; mail->forward_paths[i]; i++){
17 33 : logprintf(log, LOG_DEBUG, "Routing forward path %d: %s (%s)\n", i, mail->forward_paths[i]->path, mail->forward_paths[i]->route.router ? (mail->forward_paths[i]->route.router):"outbound");
18 33 : if(mail->forward_paths[i]->route.router){
19 : //inbound mail, apply inrouter
20 : //TODO RFC5321 4.4 (P59) says partial accepted recipient list should 200 and send failure notifications
21 33 : switch(route_local_path(log, database, mail, mail->forward_paths[i])){
22 : case 0:
23 27 : break;
24 : case 1:
25 0 : logprintf(log, LOG_WARNING, "Failed to store inbound entry for path %d (%s), deferring transaction\n", i, mail->forward_paths[i]->path);
26 0 : return 400;
27 : default:
28 6 : logprintf(log, LOG_WARNING, "Failed to route path %d (%s) inbound, rejecting transaction\n", i, mail->forward_paths[i]->path);
29 6 : return 500;
30 : }
31 : }
32 : else{
33 : //outbound mail, should have been authenticated so accept it automatically
34 0 : switch(mail_store_outbox(log, database->mail_storage.outbox_master, NULL, mail->forward_paths[i]->path, mail)){
35 : case 0:
36 0 : break;
37 : case 1:
38 0 : logprintf(log, LOG_WARNING, "Failed to store outbound entry for path %d (%s), deferring transaction\n", i, mail->forward_paths[i]->path);
39 0 : return 400;
40 : default:
41 0 : logprintf(log, LOG_WARNING, "Failed to route path %d (%s) outbound, rejecting transaction\n", i, mail->forward_paths[i]->path);
42 0 : return 500;
43 : }
44 : }
45 : }
46 :
47 25 : return rv;
48 : }
49 :
50 10 : int mail_originate(LOGGER log, char* user, MAIL* mail, MAILROUTE route, DATABASE* database){
51 10 : int rv = 250, i;
52 :
53 : //user has no routing entry, reject the mail
54 : //this should already have happened early in the conversation
55 10 : if(!route.router){
56 0 : return 500;
57 : }
58 :
59 10 : logprintf(log, LOG_INFO, "Outbound router for connected user %s is %s (%s)\n", user, route.router, route.argument?route.argument:"none");
60 :
61 10 : if(!strcmp(route.router, "drop")){
62 : //done.
63 : }
64 10 : else if(!strcmp(route.router, "handoff")){
65 0 : if(route.argument){
66 0 : for(i = 0; mail->forward_paths[i]; i++){
67 : //insert into outbound table
68 0 : logprintf(log, LOG_DEBUG, "Handing off path %d: %s\n", i, mail->forward_paths[i]->path);
69 0 : switch(mail_store_outbox(log, database->mail_storage.outbox_master, route.argument, mail->forward_paths[i]->path, mail)){
70 : case 0:
71 0 : break;
72 : case 1:
73 0 : logprintf(log, LOG_WARNING, "Failed to store handoff entry for path %d (%s), deferring transaction\n", i, mail->forward_paths[i]->path);
74 0 : return 400;
75 : default:
76 0 : logprintf(log, LOG_WARNING, "Failed to route path %d (%s) via handoff, rejecting transaction\n", i, mail->forward_paths[i]->path);
77 0 : return 500;
78 : }
79 : }
80 : }
81 : }
82 : else{
83 10 : rv = mail_route(log, mail, database);
84 : }
85 :
86 : //may check for invalid routers here
87 :
88 10 : return rv;
89 : }
90 :
91 131 : int mail_line(LOGGER log, MAIL* mail, char* line){
92 : //logprintf(log, LOG_DEBUG, "Mail line is \"%s\"\n", line);
93 131 : if(mail->data_max>0 && mail->data_offset >= mail->data_max){
94 0 : logprintf(log, LOG_INFO, "Mail length (%d) exceeded data length limit (%d), truncating\n", mail->data_offset, mail->data_max);
95 0 : return -1;
96 : }
97 :
98 131 : if(!mail->data || mail->data_allocated < mail->data_offset+strlen(line) + 3){
99 18 : mail->data = realloc(mail->data, mail->data_allocated + strlen(line) + 3);
100 18 : if(!mail->data){
101 0 : logprintf(log, LOG_ERROR, "Failed to reallocate mail buffer\n");
102 0 : return -1;
103 : }
104 18 : mail->data_allocated += strlen(line) + 3;
105 18 : logprintf(log, LOG_DEBUG, "Reallocated mail data buffer to %d bytes\n", mail->data_allocated);
106 : }
107 :
108 131 : if(mail->data_offset != 0){
109 : //insert crlf
110 100 : logprintf(log, LOG_DEBUG, "Inserting newline into data buffer\n");
111 100 : mail->data[mail->data_offset++] = '\r';
112 100 : mail->data[mail->data_offset++] = '\n';
113 : }
114 :
115 : //mind the terminator
116 131 : logprintf(log, LOG_DEBUG, "Adding %d bytes to mail at index %d\n", strlen(line), mail->data_offset);
117 131 : strncpy(mail->data + mail->data_offset, line, strlen(line) + 1);
118 131 : mail->data_offset += strlen(line);
119 131 : return 0;
120 : }
121 :
122 31 : int mail_recvheader(LOGGER log, MAIL* mail, char* announce, bool suppress_submitter){
123 : char time_buffer[SMTP_HEADER_LINE_MAX];
124 31 : char* recv_header = NULL;
125 31 : unsigned header_allocated = 0;
126 :
127 31 : unsigned mark=0, i, off=0;
128 :
129 31 : MAILPATH* forward_path = (mail->forward_paths[1]) ? NULL:mail->forward_paths[0];
130 :
131 : //create timestring
132 31 : if(common_tprintf("%a, %d %b %Y %T %z", time(NULL), time_buffer, sizeof(time_buffer) - 1) < 0){
133 0 : logprintf(log, LOG_ERROR, "Failed to get current time, buffer length probably not suitable\n");
134 0 : snprintf(time_buffer, sizeof(time_buffer)-1, "Time failed");
135 : }
136 :
137 : //write received: header
138 31 : recv_header = common_strappf(recv_header, &header_allocated,
139 : "Received: from %s by %s%s%s with %s; %s",
140 : suppress_submitter ? "remote":mail->submitter,
141 : announce,
142 : forward_path ? " for ":"",
143 : forward_path ? forward_path->path:"",
144 : mail->protocol,
145 : time_buffer);
146 :
147 31 : if(!recv_header){
148 0 : logprintf(log, LOG_ERROR, "Failed to allocate memory for Received: header\n");
149 0 : return -1;
150 : }
151 :
152 31 : logprintf(log, LOG_DEBUG, "%d bytes of header data: %s\n", header_allocated, recv_header);
153 :
154 2989 : for(i = 0; i <= header_allocated; i++){
155 2989 : if(recv_header[i] == ' '){
156 430 : mark = i;
157 : }
158 :
159 2989 : if(((i - off) >= SMTP_HEADER_LINE_MAX && off < mark) || recv_header[i] == 0){
160 : //add current contents
161 : //terminate
162 60 : if(recv_header[i]){
163 29 : recv_header[mark] = 0;
164 : }
165 60 : mail_line(log, mail, recv_header + off);
166 : //un-terminate
167 60 : recv_header[mark] = ' ';
168 :
169 60 : off = mark;
170 60 : if(recv_header[i] == 0){
171 31 : break;
172 : }
173 : }
174 : }
175 :
176 31 : free(recv_header);
177 31 : return 0;
178 : }
179 :
180 86 : int mail_reset(MAIL* mail){
181 : unsigned i;
182 :
183 86 : if(!mail){
184 0 : return -1;
185 : }
186 :
187 : //changes made here must be reflected in client_accept
188 172 : MAIL empty_mail = {
189 : .reverse_path = {
190 : .delimiter_position = 0,
191 : .in_transaction = false,
192 : .path = "",
193 : .route = {
194 : .router = NULL,
195 : .argument = NULL
196 : }
197 : },
198 : .forward_paths = {
199 : NULL
200 : },
201 : .data_offset = 0,
202 : .data_allocated = 0,
203 : .data = NULL,
204 : .submitter = NULL,
205 : //FIXME this might pose a security risk, setting to UNKNOWN might be better.
206 : //Setting to NULL breaks the constraint when inserting
207 86 : .protocol = mail->protocol,
208 : .message_id = "",
209 : .hop_count = 0,
210 : .header_offset = 0
211 : };
212 :
213 86 : empty_mail.data_allocated = mail->data_allocated;
214 86 : empty_mail.data = mail->data;
215 : //Keep submitter pointing to the submitter of the CLIENT structure
216 86 : empty_mail.submitter = mail->submitter;
217 86 : path_reset(&(mail->reverse_path));
218 :
219 86 : if(mail->data){
220 71 : mail->data[0] = 0;
221 : }
222 :
223 125 : for(i = 0; i < SMTP_MAX_RECIPIENTS && mail->forward_paths[i]; i++){
224 39 : pathpool_return(mail->forward_paths[i]);
225 : }
226 :
227 86 : *mail = empty_mail;
228 86 : return 0;
229 : }
230 :
231 9 : int mail_store_inbox(LOGGER log, sqlite3_stmt* stmt, MAIL* mail, MAILPATH* current_path){
232 : //calling contract: 0 -> ok, -1 -> fail, 1 -> defer
233 : int status;
234 :
235 9 : if(sqlite3_bind_text(stmt, 1, current_path->route.argument, -1, SQLITE_STATIC) != SQLITE_OK
236 9 : || sqlite3_bind_text(stmt, 2, mail->message_id, -1, SQLITE_STATIC) != SQLITE_OK
237 9 : || sqlite3_bind_text(stmt, 3, current_path->path, -1, SQLITE_STATIC) != SQLITE_OK
238 9 : || sqlite3_bind_text(stmt, 4, mail->reverse_path.path, -1, SQLITE_STATIC) != SQLITE_OK
239 9 : || sqlite3_bind_text(stmt, 5, mail->submitter, -1, SQLITE_STATIC) != SQLITE_OK
240 9 : || sqlite3_bind_text(stmt, 6, mail->protocol, -1, SQLITE_STATIC) != SQLITE_OK
241 9 : || sqlite3_bind_text(stmt, 7, mail->data, -1, SQLITE_STATIC) != SQLITE_OK){
242 0 : logprintf(log, LOG_ERROR, "Failed to bind mail storage parameter\n");
243 0 : sqlite3_reset(stmt);
244 0 : sqlite3_clear_bindings(stmt);
245 0 : return -1;
246 : }
247 :
248 9 : status = sqlite3_step(stmt);
249 9 : switch(status){
250 : case SQLITE_DONE:
251 9 : status = 0;
252 9 : break;
253 : case SQLITE_TOOBIG:
254 : case SQLITE_CONSTRAINT:
255 : case SQLITE_RANGE:
256 0 : status = -1;
257 0 : break;
258 : default:
259 0 : logprintf(log, LOG_INFO, "Unhandled return value from insert statement: %d\n", status);
260 0 : status = 1;
261 : }
262 :
263 9 : sqlite3_reset(stmt);
264 9 : sqlite3_clear_bindings(stmt);
265 9 : return status;
266 : }
267 :
268 10 : int mail_store_outbox(LOGGER log, sqlite3_stmt* stmt, char* mail_remote, char* envelope_to, MAIL* mail){
269 : //calling contract: 0 -> ok, -1 -> fail, 1 -> defer
270 : int status;
271 :
272 10 : if(sqlite3_bind_text(stmt, 1, mail_remote, -1, SQLITE_STATIC) != SQLITE_OK
273 10 : || sqlite3_bind_text(stmt, 2, mail->reverse_path.path, -1, SQLITE_STATIC) != SQLITE_OK
274 10 : || sqlite3_bind_text(stmt, 3, envelope_to, -1, SQLITE_STATIC) != SQLITE_OK
275 10 : || sqlite3_bind_text(stmt, 4, mail->submitter, -1, SQLITE_STATIC) != SQLITE_OK
276 10 : || sqlite3_bind_text(stmt, 5, mail->data, -1, SQLITE_STATIC) != SQLITE_OK){
277 0 : logprintf(log, LOG_ERROR, "Failed to bind mail storage parameter\n");
278 0 : sqlite3_reset(stmt);
279 0 : sqlite3_clear_bindings(stmt);
280 0 : return -1;
281 : }
282 :
283 10 : status = sqlite3_step(stmt);
284 10 : switch(status){
285 : case SQLITE_DONE:
286 10 : status = 0;
287 10 : break;
288 : case SQLITE_TOOBIG:
289 : case SQLITE_CONSTRAINT:
290 : case SQLITE_RANGE:
291 0 : status = -1;
292 0 : break;
293 : default:
294 0 : logprintf(log, LOG_INFO, "Unhandled return value from insert statement: %d\n", status);
295 0 : status = 1;
296 0 : break;
297 : }
298 :
299 10 : sqlite3_reset(stmt);
300 10 : sqlite3_clear_bindings(stmt);
301 10 : return status;
302 : }
|