技術備忘録

環境構築によるトラブルの解決方法、知った技術のまとめなどを自分のためにも書き連ねていきます。あわよくば誰かの参考になればと思います。

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を指定してあげることができます。

SMTPC言語で実装

実装したプログラムが以下のものになります。

// 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で書き込みをしてあげるだけなのでひねりがないプログラムになっています。

実行プログラム名 差出 宛先 テキスト サーバー名 ドメイン

でこのプログラムは送信までを自動的にしてくれます。

記述が汚いのでしばらくしたら書き直して修正しようとは思ってます。