Fossil SCM
Improved code to discover the IP address of the peer. Record the IP address of the peer in a Received: header line of all input emails.
Commit
9979edbdef54f547ffd2f41882691a4c60fa6df5a4fbbb9445aa6b150a73ef2f
Parent
43336f67c37beb7…
2 files changed
+21
-7
+23
+21
-7
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -1377,10 +1377,29 @@ | ||
| 1377 | 1377 | while( fossil_isspace(*zInput) ){ zInput++; } |
| 1378 | 1378 | } |
| 1379 | 1379 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 1380 | 1380 | return zResult; |
| 1381 | 1381 | } |
| 1382 | + | |
| 1383 | +/* | |
| 1384 | +** Determine the IP address on the other side of a connection. | |
| 1385 | +** Return a pointer to a string. Or return 0 if unable. | |
| 1386 | +** | |
| 1387 | +** The string is held in a static buffer that is overwritten on | |
| 1388 | +** each call. | |
| 1389 | +*/ | |
| 1390 | +char *cgi_remote_ip(int fd){ | |
| 1391 | + static char zIp[100]; | |
| 1392 | + struct sockaddr_in6 addr; | |
| 1393 | + socklen_t sz = sizeof(addr); | |
| 1394 | + if( getpeername(fd, &addr, &sz) ) return 0; | |
| 1395 | + zIp[0] = 0; | |
| 1396 | + if( inet_ntop(AF_INET6, &addr, zIp, sizeof(zIp))==0 ){ | |
| 1397 | + return 0; | |
| 1398 | + } | |
| 1399 | + return zIp; | |
| 1400 | +} | |
| 1382 | 1401 | |
| 1383 | 1402 | /* |
| 1384 | 1403 | ** This routine handles a single HTTP request which is coming in on |
| 1385 | 1404 | ** g.httpIn and which replies on g.httpOut |
| 1386 | 1405 | ** |
| @@ -1391,12 +1410,10 @@ | ||
| 1391 | 1410 | ** and subsequent code handles the actual generation of the webpage. |
| 1392 | 1411 | */ |
| 1393 | 1412 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1394 | 1413 | char *z, *zToken; |
| 1395 | 1414 | int i; |
| 1396 | - struct sockaddr_in remoteName; | |
| 1397 | - socklen_t size = sizeof(struct sockaddr_in); | |
| 1398 | 1415 | char zLine[2000]; /* A single line of input. */ |
| 1399 | 1416 | g.fullHttpReply = 1; |
| 1400 | 1417 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1401 | 1418 | malformed_request("missing HTTP header"); |
| 1402 | 1419 | } |
| @@ -1420,15 +1437,12 @@ | ||
| 1420 | 1437 | cgi_setenv("SCRIPT_NAME", ""); |
| 1421 | 1438 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1422 | 1439 | if( zToken[i] ) zToken[i++] = 0; |
| 1423 | 1440 | cgi_setenv("PATH_INFO", zToken); |
| 1424 | 1441 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1425 | - if( zIpAddr==0 && | |
| 1426 | - getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, | |
| 1427 | - &size)>=0 | |
| 1428 | - ){ | |
| 1429 | - zIpAddr = inet_ntoa(remoteName.sin_addr); | |
| 1442 | + if( zIpAddr==0 ){ | |
| 1443 | + zIpAddr = cgi_remote_ip(fileno(g.httpIn)); | |
| 1430 | 1444 | } |
| 1431 | 1445 | if( zIpAddr ){ |
| 1432 | 1446 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1433 | 1447 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1434 | 1448 | } |
| 1435 | 1449 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1377,10 +1377,29 @@ | |
| 1377 | while( fossil_isspace(*zInput) ){ zInput++; } |
| 1378 | } |
| 1379 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 1380 | return zResult; |
| 1381 | } |
| 1382 | |
| 1383 | /* |
| 1384 | ** This routine handles a single HTTP request which is coming in on |
| 1385 | ** g.httpIn and which replies on g.httpOut |
| 1386 | ** |
| @@ -1391,12 +1410,10 @@ | |
| 1391 | ** and subsequent code handles the actual generation of the webpage. |
| 1392 | */ |
| 1393 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1394 | char *z, *zToken; |
| 1395 | int i; |
| 1396 | struct sockaddr_in remoteName; |
| 1397 | socklen_t size = sizeof(struct sockaddr_in); |
| 1398 | char zLine[2000]; /* A single line of input. */ |
| 1399 | g.fullHttpReply = 1; |
| 1400 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1401 | malformed_request("missing HTTP header"); |
| 1402 | } |
| @@ -1420,15 +1437,12 @@ | |
| 1420 | cgi_setenv("SCRIPT_NAME", ""); |
| 1421 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1422 | if( zToken[i] ) zToken[i++] = 0; |
| 1423 | cgi_setenv("PATH_INFO", zToken); |
| 1424 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1425 | if( zIpAddr==0 && |
| 1426 | getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, |
| 1427 | &size)>=0 |
| 1428 | ){ |
| 1429 | zIpAddr = inet_ntoa(remoteName.sin_addr); |
| 1430 | } |
| 1431 | if( zIpAddr ){ |
| 1432 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1433 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1434 | } |
| 1435 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1377,10 +1377,29 @@ | |
| 1377 | while( fossil_isspace(*zInput) ){ zInput++; } |
| 1378 | } |
| 1379 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 1380 | return zResult; |
| 1381 | } |
| 1382 | |
| 1383 | /* |
| 1384 | ** Determine the IP address on the other side of a connection. |
| 1385 | ** Return a pointer to a string. Or return 0 if unable. |
| 1386 | ** |
| 1387 | ** The string is held in a static buffer that is overwritten on |
| 1388 | ** each call. |
| 1389 | */ |
| 1390 | char *cgi_remote_ip(int fd){ |
| 1391 | static char zIp[100]; |
| 1392 | struct sockaddr_in6 addr; |
| 1393 | socklen_t sz = sizeof(addr); |
| 1394 | if( getpeername(fd, &addr, &sz) ) return 0; |
| 1395 | zIp[0] = 0; |
| 1396 | if( inet_ntop(AF_INET6, &addr, zIp, sizeof(zIp))==0 ){ |
| 1397 | return 0; |
| 1398 | } |
| 1399 | return zIp; |
| 1400 | } |
| 1401 | |
| 1402 | /* |
| 1403 | ** This routine handles a single HTTP request which is coming in on |
| 1404 | ** g.httpIn and which replies on g.httpOut |
| 1405 | ** |
| @@ -1391,12 +1410,10 @@ | |
| 1410 | ** and subsequent code handles the actual generation of the webpage. |
| 1411 | */ |
| 1412 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1413 | char *z, *zToken; |
| 1414 | int i; |
| 1415 | char zLine[2000]; /* A single line of input. */ |
| 1416 | g.fullHttpReply = 1; |
| 1417 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1418 | malformed_request("missing HTTP header"); |
| 1419 | } |
| @@ -1420,15 +1437,12 @@ | |
| 1437 | cgi_setenv("SCRIPT_NAME", ""); |
| 1438 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1439 | if( zToken[i] ) zToken[i++] = 0; |
| 1440 | cgi_setenv("PATH_INFO", zToken); |
| 1441 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1442 | if( zIpAddr==0 ){ |
| 1443 | zIpAddr = cgi_remote_ip(fileno(g.httpIn)); |
| 1444 | } |
| 1445 | if( zIpAddr ){ |
| 1446 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1447 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1448 | } |
| 1449 |
+23
| --- src/smtp.c | ||
| +++ src/smtp.c | ||
| @@ -737,10 +737,11 @@ | ||
| 737 | 737 | ** State information for the server |
| 738 | 738 | */ |
| 739 | 739 | struct SmtpServer { |
| 740 | 740 | sqlite3_int64 idTranscript; /* Transcript ID number */ |
| 741 | 741 | sqlite3_int64 idMsg; /* Message ID number */ |
| 742 | + const char *zIpAddr; /* Remote IP address */ | |
| 742 | 743 | char *zEhlo; /* Client domain on the EHLO line */ |
| 743 | 744 | char *zFrom; /* MAIL FROM: argument */ |
| 744 | 745 | int nTo; /* Number of RCPT TO: lines seen */ |
| 745 | 746 | struct SmtpTo { |
| 746 | 747 | char *z; /* Address in each RCPT TO line */ |
| @@ -855,10 +856,18 @@ | ||
| 855 | 856 | fprintf(stderr, "C: %s", aBuf); |
| 856 | 857 | } |
| 857 | 858 | } |
| 858 | 859 | return rc; |
| 859 | 860 | } |
| 861 | + | |
| 862 | +/* | |
| 863 | +** RFC-5321 requires certain content be prepended to an email header | |
| 864 | +** as that email is received. | |
| 865 | +*/ | |
| 866 | +static void smtp_server_prepend_header_lines(SmtpServer *p){ | |
| 867 | + blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr); | |
| 868 | +} | |
| 860 | 869 | |
| 861 | 870 | /* |
| 862 | 871 | ** Capture the incoming email data into the p->msg blob. Dequote |
| 863 | 872 | ** lines of "..\r\n" into just ".\r\n". |
| 864 | 873 | */ |
| @@ -1007,10 +1016,14 @@ | ||
| 1007 | 1016 | ** |
| 1008 | 1017 | ** --dryrun Do not record any emails in the database |
| 1009 | 1018 | ** |
| 1010 | 1019 | ** --trace Print a transcript of the conversation on stderr |
| 1011 | 1020 | ** for debugging and analysis |
| 1021 | +** | |
| 1022 | +** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR | |
| 1023 | +** is the name of an environment variable, the address | |
| 1024 | +** is ready from that environment variable. | |
| 1012 | 1025 | */ |
| 1013 | 1026 | void smtp_server(void){ |
| 1014 | 1027 | char *zDbName; |
| 1015 | 1028 | const char *zDomain; |
| 1016 | 1029 | SmtpServer x; |
| @@ -1020,10 +1033,19 @@ | ||
| 1020 | 1033 | zDomain = find_option("domain",0,1); |
| 1021 | 1034 | if( zDomain==0 ) zDomain = ""; |
| 1022 | 1035 | x.srvrFlags = SMTPSRV_LOG; |
| 1023 | 1036 | if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR; |
| 1024 | 1037 | if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN; |
| 1038 | + x.zIpAddr = find_option("ipaddr",0,1); | |
| 1039 | + if( x.zIpAddr ){ | |
| 1040 | + const char *zNew = fossil_getenv(x.zIpAddr); | |
| 1041 | + if( zNew && zNew[0] ) x.zIpAddr = zNew; | |
| 1042 | + } | |
| 1043 | + if( x.zIpAddr==0 ){ | |
| 1044 | + x.zIpAddr = cgi_remote_ip(0); | |
| 1045 | + if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?"; | |
| 1046 | + } | |
| 1025 | 1047 | verify_all_options(); |
| 1026 | 1048 | if( g.argc!=3 ) usage("DBNAME"); |
| 1027 | 1049 | zDbName = g.argv[2]; |
| 1028 | 1050 | zDbName = enter_chroot_jail(zDbName, 0); |
| 1029 | 1051 | db_open_repository(zDbName); |
| @@ -1069,10 +1091,11 @@ | ||
| 1069 | 1091 | if( x.zFrom==0 || x.nTo==0 ){ |
| 1070 | 1092 | smtp_server_send(&x, "500 missing RCPT TO\r\n"); |
| 1071 | 1093 | continue; |
| 1072 | 1094 | } |
| 1073 | 1095 | smtp_server_send(&x, "354 ready\r\n"); |
| 1096 | + smtp_server_prepend_header_lines(&x); | |
| 1074 | 1097 | smtp_server_capture_data(&x, z, sizeof(z)); |
| 1075 | 1098 | smtp_server_send(&x, "250 ok\r\n"); |
| 1076 | 1099 | }else |
| 1077 | 1100 | if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){ |
| 1078 | 1101 | smtp_server_send(&x, "221 closing connection\r\n"); |
| 1079 | 1102 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -737,10 +737,11 @@ | |
| 737 | ** State information for the server |
| 738 | */ |
| 739 | struct SmtpServer { |
| 740 | sqlite3_int64 idTranscript; /* Transcript ID number */ |
| 741 | sqlite3_int64 idMsg; /* Message ID number */ |
| 742 | char *zEhlo; /* Client domain on the EHLO line */ |
| 743 | char *zFrom; /* MAIL FROM: argument */ |
| 744 | int nTo; /* Number of RCPT TO: lines seen */ |
| 745 | struct SmtpTo { |
| 746 | char *z; /* Address in each RCPT TO line */ |
| @@ -855,10 +856,18 @@ | |
| 855 | fprintf(stderr, "C: %s", aBuf); |
| 856 | } |
| 857 | } |
| 858 | return rc; |
| 859 | } |
| 860 | |
| 861 | /* |
| 862 | ** Capture the incoming email data into the p->msg blob. Dequote |
| 863 | ** lines of "..\r\n" into just ".\r\n". |
| 864 | */ |
| @@ -1007,10 +1016,14 @@ | |
| 1007 | ** |
| 1008 | ** --dryrun Do not record any emails in the database |
| 1009 | ** |
| 1010 | ** --trace Print a transcript of the conversation on stderr |
| 1011 | ** for debugging and analysis |
| 1012 | */ |
| 1013 | void smtp_server(void){ |
| 1014 | char *zDbName; |
| 1015 | const char *zDomain; |
| 1016 | SmtpServer x; |
| @@ -1020,10 +1033,19 @@ | |
| 1020 | zDomain = find_option("domain",0,1); |
| 1021 | if( zDomain==0 ) zDomain = ""; |
| 1022 | x.srvrFlags = SMTPSRV_LOG; |
| 1023 | if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR; |
| 1024 | if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN; |
| 1025 | verify_all_options(); |
| 1026 | if( g.argc!=3 ) usage("DBNAME"); |
| 1027 | zDbName = g.argv[2]; |
| 1028 | zDbName = enter_chroot_jail(zDbName, 0); |
| 1029 | db_open_repository(zDbName); |
| @@ -1069,10 +1091,11 @@ | |
| 1069 | if( x.zFrom==0 || x.nTo==0 ){ |
| 1070 | smtp_server_send(&x, "500 missing RCPT TO\r\n"); |
| 1071 | continue; |
| 1072 | } |
| 1073 | smtp_server_send(&x, "354 ready\r\n"); |
| 1074 | smtp_server_capture_data(&x, z, sizeof(z)); |
| 1075 | smtp_server_send(&x, "250 ok\r\n"); |
| 1076 | }else |
| 1077 | if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){ |
| 1078 | smtp_server_send(&x, "221 closing connection\r\n"); |
| 1079 |
| --- src/smtp.c | |
| +++ src/smtp.c | |
| @@ -737,10 +737,11 @@ | |
| 737 | ** State information for the server |
| 738 | */ |
| 739 | struct SmtpServer { |
| 740 | sqlite3_int64 idTranscript; /* Transcript ID number */ |
| 741 | sqlite3_int64 idMsg; /* Message ID number */ |
| 742 | const char *zIpAddr; /* Remote IP address */ |
| 743 | char *zEhlo; /* Client domain on the EHLO line */ |
| 744 | char *zFrom; /* MAIL FROM: argument */ |
| 745 | int nTo; /* Number of RCPT TO: lines seen */ |
| 746 | struct SmtpTo { |
| 747 | char *z; /* Address in each RCPT TO line */ |
| @@ -855,10 +856,18 @@ | |
| 856 | fprintf(stderr, "C: %s", aBuf); |
| 857 | } |
| 858 | } |
| 859 | return rc; |
| 860 | } |
| 861 | |
| 862 | /* |
| 863 | ** RFC-5321 requires certain content be prepended to an email header |
| 864 | ** as that email is received. |
| 865 | */ |
| 866 | static void smtp_server_prepend_header_lines(SmtpServer *p){ |
| 867 | blob_appendf(&p->msg, "Received: from %s by Fossil-smtp\r\n", p->zIpAddr); |
| 868 | } |
| 869 | |
| 870 | /* |
| 871 | ** Capture the incoming email data into the p->msg blob. Dequote |
| 872 | ** lines of "..\r\n" into just ".\r\n". |
| 873 | */ |
| @@ -1007,10 +1016,14 @@ | |
| 1016 | ** |
| 1017 | ** --dryrun Do not record any emails in the database |
| 1018 | ** |
| 1019 | ** --trace Print a transcript of the conversation on stderr |
| 1020 | ** for debugging and analysis |
| 1021 | ** |
| 1022 | ** --ipaddr ADDR The SMTP connection originates at ADDR. Or if ADDR |
| 1023 | ** is the name of an environment variable, the address |
| 1024 | ** is ready from that environment variable. |
| 1025 | */ |
| 1026 | void smtp_server(void){ |
| 1027 | char *zDbName; |
| 1028 | const char *zDomain; |
| 1029 | SmtpServer x; |
| @@ -1020,10 +1033,19 @@ | |
| 1033 | zDomain = find_option("domain",0,1); |
| 1034 | if( zDomain==0 ) zDomain = ""; |
| 1035 | x.srvrFlags = SMTPSRV_LOG; |
| 1036 | if( find_option("trace",0,0)!=0 ) x.srvrFlags |= SMTPSRV_STDERR; |
| 1037 | if( find_option("dryrun",0,0)!=0 ) x.srvrFlags |= SMTPSRV_DRYRUN; |
| 1038 | x.zIpAddr = find_option("ipaddr",0,1); |
| 1039 | if( x.zIpAddr ){ |
| 1040 | const char *zNew = fossil_getenv(x.zIpAddr); |
| 1041 | if( zNew && zNew[0] ) x.zIpAddr = zNew; |
| 1042 | } |
| 1043 | if( x.zIpAddr==0 ){ |
| 1044 | x.zIpAddr = cgi_remote_ip(0); |
| 1045 | if( x.zIpAddr==0 ) x.zIpAddr = "?.?.?.?"; |
| 1046 | } |
| 1047 | verify_all_options(); |
| 1048 | if( g.argc!=3 ) usage("DBNAME"); |
| 1049 | zDbName = g.argv[2]; |
| 1050 | zDbName = enter_chroot_jail(zDbName, 0); |
| 1051 | db_open_repository(zDbName); |
| @@ -1069,10 +1091,11 @@ | |
| 1091 | if( x.zFrom==0 || x.nTo==0 ){ |
| 1092 | smtp_server_send(&x, "500 missing RCPT TO\r\n"); |
| 1093 | continue; |
| 1094 | } |
| 1095 | smtp_server_send(&x, "354 ready\r\n"); |
| 1096 | smtp_server_prepend_header_lines(&x); |
| 1097 | smtp_server_capture_data(&x, z, sizeof(z)); |
| 1098 | smtp_server_send(&x, "250 ok\r\n"); |
| 1099 | }else |
| 1100 | if( strncmp(z, "QUIT", 4)==0 && fossil_isspace(z[4]) ){ |
| 1101 | smtp_server_send(&x, "221 closing connection\r\n"); |
| 1102 |