C言語でSMTPを実装してみる
SMTPプロコトル
SMTPはSimple Mail Transfer Protocol と呼ばれ、メールの送信に使われるプロコトルです。 Wikipediaによると
Simple Mail Transfer Protocol(シンプル メール トランスファー プロトコル、SMTP)または簡易メール転送プロトコルは、インターネットで電子メールを転送するプロトコルである。
みたいです。
このプロコトルはサーバーにTCPで25番ポートで接続します。 以下はコマンドライン上で行なった結果です。
$SERVER_NAMEは使ったメールサーバーです。
$DOMAINは~.jpや~.comのようなものです。
$FROMは送信するアドレス、$RCPTは送り先のアドレスを指定しています。
TryingのあとはサーバのIPアドレスなので伏せています。
$ telnet $SERVER_NAME 25 Trying ***.**.**.***... Connected to $SERVER_NAME. Escape character is '^]'. 220 $SERVER_NAME ESMTP HELO $DOMAIN 250 $SERVER_NAME MAIL FROM: $FROM 250 2.1.0 Ok RCPT TO: $RCPT 250 2.1.5 Ok DATA 354 End data with . test message 250 2.0.0 Ok: queued as B982D140477 QUIT 221 2.0.0 Bye Connection closed by foreign host.
DATAは送りたいメッセージです。改行後の.でメッセージを終了します。 ここで「250 2.0.0 Ok: queued as B982D140477」が受信されるとメールが送信されます。 ただしこのままだとFROMやTOが定義されないのでメールの受信者がどこから誰に宛てているのか分かりません。 定義するためにはFROM: 〜、TO: 〜を描いてあげる必要があります。なのでDATAを
DATA 354 End data with . From: $FROM TO: $RCPT testmessage . 250 2.0.0 Ok: queued as B982D140477
としてあげることでFROM、TOを指定してあげることができます。
SMTPをC言語で実装
実装したプログラムが以下のものになります。
// SMTPの基本プロコトルをC言語で実装 // 整えてプログラムしてないのでかなり見にくい #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #include <netdb.h> #include <unistd.h> #define ERR -1 #define BUFSIZE 1024 void usage(void); char *command_name; int socket(int domain, int type, int protocol); int main(int argc, char* argv[]){ int sds; //ソケットディスクリプタ FILE *fp; struct sockaddr_in server;//サーバプロセスのソケットアドレス情報 struct hostent *hp; //ホスト情報 char buf[BUFSIZE]; char *mes; if(argc != 6) usage(); /*ソケットの作成*/ if((sds = socket(PF_INET, SOCK_STREAM, 0)) == ERR){ perror("client: socket"); exit(1); } /*サーバの情報を格納*/ memset((void*)&server, 0, sizeof(server)); server.sin_family = PF_INET; server.sin_port = htons(25); //ポート番号25 // printf("%d", server.sin_family); /*IPアドレスの設定*/ hp = gethostbyname(argv[4]); memcpy(&(server.sin_addr), hp->h_addr_list[0], hp->h_length); /*サーバに接続*/ if(connect(sds, (struct sockaddr *)&server, sizeof(server))==ERR){ perror("client: connect"); exit(1); } read(sds, buf, sizeof(buf)); printf("%s", buf); /* HELOの送信 */ strcpy(buf,"HELO "); strcat(buf,argv[5]); strcat(buf,"\n"); printf("書き込む内容//%s",buf); write(sds, buf, strlen(buf)); read(sds, buf, sizeof(buf)); printf("読み込んだ内容//%s", buf); /* MAIL FROMの送信 */ strcpy(buf, "MAIL FROM: "); strcat(buf,argv[1]); strcat(buf, "\r\n"); printf("書き込む内容//%s",buf); write(sds, buf, strlen(buf)); read(sds, buf, sizeof(buf)); printf("読み込んだ内容//%s", buf); /* RCPT TOの送信 */ strcpy(buf, "RCPT TO: "); strcat(buf, argv[2]); strcat(buf, "\r\n"); printf("書き込む内容//%s",buf); write(sds, buf, strlen(buf)); read(sds, buf, sizeof(buf)); printf("読み込んだ内容//%s", buf); strcpy(buf,"DATA\r\n"); printf("書き込む内容//%s",buf); write(sds, buf, strlen(buf)); read(sds, buf, sizeof(buf)); printf("読み込んだ内容//%s", buf); strcpy(buf,"From:"); strcat(buf,argv[1]); strcat(buf,"\r\n"); strcat(buf,"To:"); strcat(buf,argv[2]); strcat(buf,"\n"); strcat(buf,argv[3]); strcat(buf,"\r\n"); printf("書き込む内容//%s",buf); write(sds, buf, strlen(buf)); strcpy(buf,".\r\n"); write(sds, buf, strlen(buf)); printf("書き込む内容//%s",buf); read(sds, buf, sizeof(buf)); printf("読み込んだ内容//%s", buf); write(sds, "QUIT", sizeof(buf)); read(sds, write, sizeof(buf)); return 0; } void usage(void) { fprintf(stderr, "Usage: %s 差出 宛先 テキスト サーバー名 ドメイン\n",command_name); exit(1); }
ソケットを使うのでソケットプログラムが必要です。 サーバに接続したらwriteで書き込みをしてあげるだけなのでひねりがないプログラムになっています。
実行プログラム名 差出 宛先 テキスト サーバー名 ドメイン
でこのプログラムは送信までを自動的にしてくれます。
記述が汚いのでしばらくしたら書き直して修正しようとは思ってます。