00001
00011 #include <netdb.h>
00012 #include <stdio.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <sys/socket.h>
00016 #include <unistd.h>
00017 #include <sys/types.h>
00018 #include <ctype.h>
00019
00020 #include <regex.h>
00021 #include <locale.h>
00022
00023 #include <errno.h>
00024
00026 #define ERROR_NETWORK 3
00027
00028 #define ERROR_PARAM 4
00029
00030 #define ERROR_CODING 5
00031
00032 #define RET_OPENRELAY 1
00033 #define RET_NOT_OPEN_RELAY 0
00034
00035 struct param_t {
00036 int help;
00037 int verbose;
00038 char *host;
00039 char *from;
00040 char *to;
00041 char *comment;
00042 int timeout;
00043 int port;
00044 };
00045
00046 int mailaddr_test(char *);
00047
00053 void get_params(int argc, char *argv[], struct param_t *p) {
00054 int c;
00055 opterr = 0;
00056
00058 while ((c = getopt (argc, argv, "hvf:u:c:t:")) != -1) {
00059 switch (c) {
00060 case 'h':
00061 p->help = 1;
00062 break;
00063 case 'v':
00064 p->verbose = 1;
00065 break;
00067 case 'f':
00068 p->from = optarg;
00069 if (0 != mailaddr_test(p->from)) {
00070 fprintf(stderr, "Invalid sender address. (%s)\n", p->from);
00071 exit(ERROR_PARAM);
00072 }
00073 break;
00075 case 'u':
00076 p->to = optarg;
00077 if (0 != mailaddr_test(p->to)) {
00078 fprintf(stderr, "Invalid recipient address.\n");
00079 exit(ERROR_PARAM);
00080 }
00081 break;
00083 case 'c':
00084 p->comment = optarg;
00085 break;
00086
00087 case 't':
00088 p->timeout = atoi(optarg);
00089 if (p->timeout < 0 || p->timeout > 60) {
00090 fprintf(stderr, "Invalid timeout: %s (interpreted as %d)\n",
00091 optarg, p->timeout);
00092 exit(ERROR_PARAM);
00093 }
00094 break;
00096 case '?':
00097 if (isprint (optopt)) {
00098 fprintf(stderr, "Unknown option `-%c'.\n", optopt);
00099 } else {
00100 fprintf(stderr, "Unknown option character `\\x%x'.\n",
00101 optopt);
00102 }
00103 exit(ERROR_PARAM);
00104 break;
00105 default:
00106 fprintf(stderr, "Unknown getopt() action.\n");
00107 exit(ERROR_CODING);
00108 }
00109 }
00110
00114 if (optind < argc) {
00115 p->host = argv[optind];
00116 } else {
00117 fprintf(stderr, "Empty hostname.\n");
00118 exit(ERROR_PARAM);
00119 }
00120
00121
00126 optind++;
00127 if (optind < argc) {
00128 int tp;
00129 tp = atoi(argv[optind]);
00130 if ((tp < 1) || (tp > 65535)) {
00131 fprintf(stderr, "Invalid port number: %s (interpreted as %d)\n",
00132 argv[optind], tp);
00133 exit(ERROR_PARAM);
00134 } else {
00135 p->port = tp;
00136 }
00137 }
00138
00142 optind++;
00143 if (optind < argc) {
00144 fprintf(stderr, "Unknown parameter: %s \n", argv[optind]);
00145 exit(ERROR_PARAM);
00146 }
00147
00148 return;
00149 }
00150
00153 void default_params(struct param_t *p) {
00154 memset(p, 0, sizeof(struct param_t));
00155
00156 p->help = 0;
00157 p->verbose = 0;
00158 p->from = p->to = p->comment = NULL;
00159 p->timeout = 60;
00160 p->port = 25;
00161 return;
00162 }
00163
00171 void print_message(char from, char* msg_text) {
00172 const char delimiters[] = "\n";
00173 char *token, *cp;
00174 int i = 0;
00175
00176 cp = strdup(msg_text);
00177 if (NULL == cp) {
00178 fprintf(stderr, "There is not enough memory (strdup).");
00179 exit(ERROR_CODING);
00180 }
00181
00183 while (cp[i] != '\0') {
00184 if ('\r' == cp[i]) {
00185 cp[i] = ' ';
00186 }
00187 i++;
00188 }
00189
00191 token = strtok(cp, delimiters);
00192 if (NULL == token) {
00193 fprintf(stderr, "Invalid data? (print_message)\n");
00194 } else {
00195 do {
00196 if ('S' == from) {
00197 printf("<<<");
00198 } else {
00199 printf(">>>");
00200 }
00201 printf(" %s\n", token);
00202 } while (NULL != (token = strtok(NULL, delimiters)));
00203 }
00204 free(cp);
00205 }
00206
00207
00214 int send_message(int sock, char *msg_text, int verbose) {
00215
00216 if (1 == verbose) print_message('C', msg_text);
00217
00218 if (send(sock, msg_text, strlen(msg_text), 0) < 0) {
00219 perror("send");
00220 return -1;
00221 }
00222 return 0;
00223 }
00224
00230 int rt_quit(int sock, int ret, int verbose) {
00231
00232 if (-1 != sock) {
00233 send_message(sock, "\r\nQUIT\r\n", verbose);
00234 close(sock);
00235 }
00236
00237 if (1 == verbose) {
00238 printf("return code: %d\n", ret);
00239 switch (ret) {
00240 case ERROR_NETWORK:
00241 printf("Network error.\n");
00242 break;
00243 case ERROR_PARAM:
00244 printf("Error around the paramter(s).\n");
00245 break;
00246 case ERROR_CODING:
00247 printf("Internal error.\n");
00248 break;
00249 case RET_OPENRELAY:
00250 printf("The host is an open-relay server.\n");
00251 break;
00252 case RET_NOT_OPEN_RELAY:
00253 printf("The host is NOT an open-relay server.\n");
00254 break;
00255 default:
00256 printf("Unknown return value.\n");
00257 break;
00258 }
00259 }
00260
00261 exit(ret);
00262 }
00263
00274 int receive_message(int sock, int answer_ok, char *msg_text, int msg_len, int timeout, int verbose) {
00275 int rcvlen;
00276 char msg_num[8] = {0};
00277 int msg_code = 0;
00278 int i;
00279
00280 struct timeval tv;
00281 fd_set read_fds;
00282 int fdmax;
00283 int retval;
00284
00285 FD_ZERO(&read_fds);
00286 FD_SET(sock, &read_fds);
00287 fdmax = sock;
00288
00289 tv.tv_sec = timeout;
00290 tv.tv_usec = 0;
00291
00293 retval = select(fdmax + 1, &read_fds, NULL, NULL, &tv);
00294 if (-1 == retval) {
00295 perror("select");
00296 rt_quit(sock, ERROR_NETWORK, verbose);
00297 }
00298
00299 if (FD_ISSET(sock, &read_fds)) {
00300 rcvlen = recv(sock, msg_text, msg_len, 0);
00301 } else {
00302 rt_quit(sock, ERROR_NETWORK, verbose);
00303 }
00304
00305 if (rcvlen < 0) {
00306 perror("recv");
00307 rt_quit(sock, ERROR_NETWORK, verbose);
00308 } else if (0 == rcvlen) {
00309 fprintf(stderr, "Connection reset by peer.\n");
00310 rt_quit(-1, ERROR_NETWORK, verbose);
00311 } else {
00312 msg_text[rcvlen] = '\0';
00313 if (1 == verbose) print_message('S', msg_text);
00314
00315 if (NULL != (strchr(msg_text, ' '))) {
00318 i = 0;
00319 while ((msg_text[i] != ' ') && (msg_text[i] != '\0')) {
00320 i++;
00321
00322 if (i > 5) {
00323 fprintf(stderr, "Invalid answer from the SMTP server.\n");
00324 rt_quit(sock, ERROR_NETWORK, verbose);
00325 }
00326 }
00327
00328 i = (i > sizeof(msg_num) - 1) ? sizeof(msg_num) - 1 : i;
00329 strncpy(msg_num, msg_text, i);
00330
00331 msg_code = atoi(msg_num);
00332
00334 if (msg_code > 999 || msg_code < 100) {
00335 fprintf(stderr, "Invalid answer from the SMTP server.\n");
00336 rt_quit(sock, ERROR_NETWORK, verbose);
00337 }
00338
00341 if (550 == msg_code || 554 == msg_code) {
00342 rt_quit(sock, RET_NOT_OPEN_RELAY, verbose);
00343 }
00344
00345 if ((msg_code != answer_ok) && (0 != answer_ok)) {
00346 fprintf(stderr, "Invalid answer from the SMTP server.\n");
00347 if (1 != verbose) {
00348 print_message('S', msg_text);
00349 }
00350 rt_quit(sock, ERROR_NETWORK, verbose);
00351 }
00352
00353 } else {
00354 fprintf(stderr, "Invalid answer from the SMTP server.\n");
00355 rt_quit(sock, ERROR_NETWORK, verbose);
00356 }
00357 }
00358
00359 return msg_code;
00360 }
00361
00364 void usage(void) {
00365 printf("Usage:\topenrt [-v] [-t timeout] -f sender_addr ");
00366 printf("-u recipient_addr [-c comment] <hostname> <port>\n");
00367 printf("\topenrt -h\n");
00368 printf("Options:\n \
00369 -h\t\t\tthis help\n \
00370 -f sender_addr\t\tsender-addr\n \
00371 -u recipient_addr\tto-addr\n \
00372 -v\t\t\tverbose mode\n \
00373 -c comment\t\textra comments to the mail\n \
00374 -t timeout\t\ttimeout in second(s)\n");
00375 }
00376
00383 int test_smtp(int sock, struct param_t *params) {
00384 int ret = 0;
00385 char msg_text[512];
00386 char tmp_text[2048];
00387 char host[256];
00388
00390 ret = receive_message(sock, 220, msg_text, sizeof(msg_text), params->timeout, params->verbose);
00391
00393 if (-1 == gethostname(host, sizeof(host))) {
00394 fprintf(stderr, "Couldn't get hostname (gethostname()).\n");
00395 exit(ERROR_CODING);
00396 }
00397
00402 ret = snprintf(tmp_text, sizeof(tmp_text), "HELO %s\r\n", host);
00403 if (ret < 0) {
00404 fprintf(stderr, "Internal error (snprintf).");
00405 exit(ERROR_CODING);
00406 }
00407 send_message(sock, tmp_text, params->verbose);
00408
00410 receive_message(sock, 250, msg_text, sizeof(msg_text), params->timeout, params->verbose);
00411
00413 ret = snprintf(tmp_text, sizeof(tmp_text), "MAIL FROM: %s\r\n", params->from);
00414 if (ret < 0) {
00415 fprintf(stderr, "Internal error (snprintf).");
00416 return ERROR_CODING;
00417 }
00418
00419 send_message(sock, tmp_text, params->verbose);
00420 receive_message(sock, 250, msg_text, sizeof(msg_text), params->timeout, params->verbose);
00421
00423 ret = snprintf(tmp_text, sizeof(tmp_text), "RCPT TO: %s\r\n", params->to);
00424 if (ret < 0) {
00425 fprintf(stderr, "Internal error (snprintf).");
00426 return ERROR_CODING;
00427 }
00428
00429 send_message(sock, tmp_text, params->verbose);
00430 ret = receive_message(sock, 0, msg_text, sizeof(msg_text), params->timeout, params->verbose);
00431
00432
00433 send_message(sock, "DATA\r\n", params->verbose);
00434 receive_message(sock, 354, msg_text, sizeof(msg_text), params->timeout, params->verbose);
00435
00437 ret = snprintf(tmp_text, sizeof(tmp_text), "To: %s\r\n" \
00438 "From: %s\r\n" \
00439 "Subject: open-relay test message\r\n" \
00440 "\r\n" \
00441 "This is a test message which check your SMTP server as an\r\n" \
00442 "open-relay server and try to send a message with its.\r\n" \
00443 "\r\n" \
00444 "Please ignore this e-mail.\r\n" \
00445 "\r\n" \
00446 "%s\r\n" \
00447 "\r\n" \
00448 "EOF\r\n" \
00449 ".\r\n", params->to, params->from, params->comment);
00450 if (ret < 0) {
00451 fprintf(stderr, "Internal error (snprintf).");
00452 return ERROR_CODING;
00453 }
00454
00455 send_message(sock, tmp_text, params->verbose);
00456 ret = receive_message(sock, 0, msg_text, sizeof(msg_text), params->timeout, params->verbose);
00457
00458 if (250 == ret) {
00459 ret = RET_OPENRELAY;
00460 } else {
00462 ret = RET_NOT_OPEN_RELAY;
00463 }
00464
00465 return ret;
00466 }
00467
00473 int mailaddr_test(char *mailaddr) {
00474 int ret;
00475 regex_t ford;
00476
00477 ret = 0;
00478
00480 if (NULL == mailaddr) {
00481 return 1;
00482 }
00483
00485 if (regcomp(&ford,
00486 "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*$",
00487 REG_EXTENDED) != 0) {
00489 fprintf(stderr, "Internal error in the regular expression.\n");
00490 exit(ERROR_CODING);
00491 }
00492
00493 if ((ret = regexec(&ford, mailaddr, 0, NULL, 0)) == 0) {
00494 ret = 0;
00495 } else {
00496 ret = 1;
00497 }
00498
00499 regfree(&ford);
00500 return ret;
00501 }
00502
00505 int main(int argc, char *argv[]) {
00506 struct hostent *host;
00507 struct sockaddr_in addr;
00508
00509 int ret;
00510 int sock;
00511
00512 struct param_t params;
00513
00515 if ((0 == geteuid()) || (0 == getegid())) {
00516 fprintf(stderr, "Don't use this program as root.\n");
00517 exit(ERROR_PARAM);
00518 }
00519
00521 if (2 > argc) {
00522 usage();
00523 exit(0);
00524 }
00525
00527 default_params(¶ms);
00528 get_params(argc, argv, ¶ms);
00529
00530 if (1 == params.help) {
00531 usage();
00532 exit(0);
00533 }
00534
00535 if (NULL == params.host || NULL == (host = gethostbyname(params.host))) {
00536 fprintf(stderr, "Invalid hostname.\n");
00537 exit(ERROR_PARAM);
00538 }
00539
00540 if (0 != params.verbose) {
00541 printf("Connection to %s...\n", params.host);
00542 }
00543
00544 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
00545 perror("socket");
00546 exit(ERROR_NETWORK);
00547 }
00548
00549 memset(&addr, 0, sizeof(addr));
00550 addr.sin_family = AF_INET;
00551 addr.sin_port = htons(params.port);
00552 addr.sin_addr = *((struct in_addr *) (host->h_addr));
00553
00554 if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00555 perror("connect");
00556 exit(ERROR_NETWORK);
00557 }
00558
00559 if (0 != params.verbose) {
00560 printf("Connection established.\n");
00561 }
00562
00564 ret = test_smtp(sock, ¶ms);
00565
00567 rt_quit(sock, ret, params.verbose);
00568
00569 return 0;
00570 }