Apply what you learned about sockets to send email from your own program.
| SMTP command | Meaning | Example |
|---|---|---|
HELO | Greeting (announce our hostname) | HELO myhost |
MAIL FROM: | Sender address | MAIL FROM:<sender@example.com> |
RCPT TO: | Recipient address | RCPT TO:<receiver@example.com> |
DATA | Begin the message body | DATA |
. | End the message body | . (a line with only a period) |
QUIT | End the session | QUIT |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <netdb.h> #include <sys/socket.h> #define BUF_SIZE 4096 // send a command and print the server's response void smtp_command(int sock, const char *cmd) { char buf[BUF_SIZE]; if (cmd != NULL) { send(sock, cmd, strlen(cmd), 0); printf("C: %s", cmd); } int n = recv(sock, buf, BUF_SIZE - 1, 0); if (n > 0) { buf[n] = '\0'; printf("S: %s", buf); } } // connect to an SMTP server int connect_smtp(const char *host, int port) { struct hostent *h = gethostbyname(host); if (!h) { fprintf(stderr, "Host lookup failed\n"); return -1; } int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); memcpy(&addr.sin_addr.s_addr, h->h_addr, h->h_length); if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("connect"); close(sock); return -1; } return sock; }
int main(void) { const char *smtp_host = "mail.example.com"; const char *from = "alice@example.com"; const char *to = "bob@example.com"; // 1. connect int sock = connect_smtp(smtp_host, 25); if (sock < 0) return 1; // 2. read greeting smtp_command(sock, NULL); // 3. SMTP session smtp_command(sock, "HELO mycomputer\r\n"); char cmd[BUF_SIZE]; snprintf(cmd, BUF_SIZE, "MAIL FROM:<%s>\r\n", from); smtp_command(sock, cmd); snprintf(cmd, BUF_SIZE, "RCPT TO:<%s>\r\n", to); smtp_command(sock, cmd); smtp_command(sock, "DATA\r\n"); // 4. headers and body snprintf(cmd, BUF_SIZE, "Subject: Test from C program\r\n" "From: %s\r\n" "To: %s\r\n" "\r\n" // blank line ends headers "Hello!\r\n" "This mail was sent from a C program.\r\n" ".\r\n", // line with only a period ends the body from, to); smtp_command(sock, cmd); // 5. end session smtp_command(sock, "QUIT\r\n"); close(sock); printf("\nMail sent.\n"); return 0; }
fgets to let the user type the body interactively.// read body from the keyboard printf("Enter mail body (blank line to finish):\n"); char body[BUF_SIZE] = ""; char line[256]; while (1) { fgets(line, 256, stdin); if (line[0] == '\n') break; // blank line to finish strcat(body, line); }
// address book const char *addressbook[] = { "user1@example.com", "user2@example.com", "user3@example.com" }; int num_addr = 3; printf("Select recipient:\n"); for (int i = 0; i < num_addr; i++) printf(" %d: %s\n", i + 1, addressbook[i]); int choice; scanf("%d", &choice); const char *to = addressbook[choice - 1];
// read body from a text file FILE *fp = fopen("message.txt", "r"); if (fp == NULL) { perror("fopen"); return 1; } char body[BUF_SIZE] = ""; char line[256]; while (fgets(line, 256, fp) != NULL) strcat(body, line); fclose(fp);
smtp_command so it checks server status codes (250, 354, etc.). If the response is unexpected, print an error and abort.RCPT TO multiple times delivers one message to several addresses. Allow multi-selection from the address book.fgets and insert it into the DATA payload.Check your understanding of this lesson!
HELO/EHLO announces your hostname. EHLO is the extended form and also returns a list of supported features.
Port 25 is standard SMTP. Submission uses 587, and SMTPS typically uses 465.
After DATA, a line containing nothing but a period signals the end of the message body.