To fill the server address, I use this function
// avp #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef WIN32 // gcc ... -lws2_32 -lwsock32 #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #else #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #endif /* make ip-addr & port in allocated struct sockaddr returns 0 - OK, 1 - unknown host, 2 - bad port (service) */ int make_tcpservaddr (char *host, char *port, struct sockaddr_in *psa) { /* *psa an Internet endpoint address */ struct hostent *phe; /* pointer to host information entry */ struct servent *pse; /* pointer to service information entry */ psa->sin_family = AF_INET; if (pse = getservbyname(port,"tcp")) psa->sin_port = pse->s_port; else if ((psa->sin_port = htons((unsigned short)atoi(port))) == 0) { return 2; } if (host == NULL || *host == 0 || strcasecmp(host,"inaddrany") == 0 || strcasecmp(host,"inaddr_any") == 0 || strcasecmp(host,"255.255.255.255") == 0 || strcasecmp(host,"0.0.0.0") == 0 ) psa->sin_addr.s_addr = INADDR_ANY; else if (phe = gethostbyname(host)) memcpy (&(psa->sin_addr),phe->h_addr, phe->h_length); else if ((psa->sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) { return 1; } return 0; // OK }
It fills the struct sockaddr_in with the IP address and port number. Call:
struct sockaddr_in addr; ... switch (make_tcpservaddr(host, port, &addr)) { case 0: break; // OK .... }
Then you can use the addr
structure either to connect the client to the server
if (connect(cli_sock,(struct sockaddr *)&addr,sizeof(addr)) < 0) { perror("connect"); }
either for server
if (bind(serv_sock,(struct sockaddr *)&addr,sizeof(addr)) < 0) { perror ("bind"); }
Obviously, error handling may be different. For example, for the server, in the case of errno == EADDRINUSE, several attempts can be made bind ()
Regarding ESMTP / SMTP conversation (port 25)
First, always check the server response code (the first 3 characters are the response code). If the first character of answer 2 is normal.
Always check for the word ESMTP in the server greeting. If it does not exist (now you can hardly see it, but still ...), then the SMTP protocol, and not ESMTP. For the SMTP protocol, you must answer with the HELO line, and for ESMTP, the EHLO.
Here is an example (as you can see the login-password is not at all mandatory. Its necessity is revealed during the initial dialogue with the server)
220 mail.bigtelecom.ru ESMTP Postfix ehlo scan.ru 250-mail.bigtelecom.ru 250-PIPELINING 250-SIZE 30720000 250-ETRN 250-STARTTLS 250-AUTH LOGIN PLAIN 250-AUTH=LOGIN PLAIN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN mail from: avp@scan.ru 250 2.1.0 Ok quit 221 2.0.0 Bye
Well, here read the RFC for SMTP / ESMTP
A couple of important notes. Mail protocol is string oriented . End dates are 2 characters \ r \ n. But, some clients (and possibly servers) in practice neglect the \ r symbol (and sometimes). This must be considered when parsing the protocol.
The second is also associated with string orientation. The recv () function (unfortunately) can return either a part of a string or several lines. Therefore, you will have to translate the received data into a stream of lines (well, or do fdopen () (it works strangely only in Windows - an incentive to drop it)).
Successes. Ask if I remember anything else about the mail - I will help.
Base64 encoding-decoding functions with multiple call options from main (in #if ... #else ... #endif).
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef WIN32 typedef unsigned char u_char; #endif /* If the user agent wishes to send the userid "Aladdin" and password "open sesame", it would use the following header field: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== http://base64.sourceforge.net/b64.c */ /* ** Translation Table as described in RFC1113 */ static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // 3 bytes (len) to 4 base64 bytes void encode_b64block (u_char *in, u_char *out, int len) { out[0] = cb64[in[0] >> 2]; out[1] = cb64[((in[0] << 4) | (in[1] >> 4)) & 0x3f]; out[2] = (len > 1) ? cb64[((in[1] << 2) | (in[2] >> 6)) & 0x3f] : '='; out[3] = (len > 2) ? cb64[in[2] & 0x3f] : '='; } /* Кодирует ilen байт в base64 Returns: количество байт, помещенных в b64out (max oblen) в *ires количество закодированых байт из inpch8 */ int encode_b64 (u_char *inpch8, int ilen, u_char *b64out, int oblen, int *ires) { int i, l = 0, bound = (oblen/4)*3; // байты входа, размещаемые в выходе if (bound > ilen) bound = ilen; bound = (bound/3)*3; for (i = 0; i < bound; i += 3, l += 4) { encode_b64block (inpch8+i, b64out+l, 3); } if (i < ilen) { if (oblen-l > 3) { int len = ilen-i; encode_b64block (inpch8+i, b64out+l, len); l += 4; if (len > 2) i += 3; else if (len > 1) i += 2; else i++; } } if (ires) *ires = i; return l; } static u_char decodetab[256]; static int initdecode = 0; // 4 (len) base64 bytes to 8-bit bytes. // Returns number of output bytes int decode_b64block (u_char *in, u_char *out, int len) { if (len < 2 || in[0] == '=' || in[1] == '=' || in[0] == 0 || in[1] == 0) return 0; if (!initdecode) { int i; for (i = 0; i < 256; i++) decodetab[i] = 0xff; for (i = 0; cb64[i]; i++) decodetab[(u_char)cb64[i]] = i; initdecode = 1; } out[0] = (decodetab[in[0]] << 2) | (decodetab[in[1]] >> 4); if (len == 2 || in[2] == '=' || in[2] == 0) return 1; out[1] = (decodetab[in[1]] << 4) | (decodetab[in[2]] >> 2); if (len == 3 || in[3] == '=') return 2; out[2] = (decodetab[in[2]] << 6) | decodetab[in[3]]; return 3; } // 4 (len) decodetab[] base64 bytes to 8-bit bytes. // Returns number of output bytes int decode_true_b64block (u_char *in, u_char *out, int len) { if (len < 2) return 0; out[0] = (in[0]<<2) | (in[1] >> 4); if (len == 2) return 1; out[1] = (in[1]<<4) | (in[2]>>2); if (len == 3) return 2; out[2] = (in[2]<<6) | in[3]; return 3; } // decode up to '=' or '\0' skipping "garbage" // Returns output length int decode_b64str (u_char *inb64, u_char *out, int olmax, u_char **endinp) { int i, len = 0, bound = olmax-3, go = 1, l; if (!initdecode) { for (i = 0; i < 256; i++) decodetab[i] = 0xff; for (i = 0; cb64[i]; i++) decodetab[(u_char)cb64[i]] = i; initdecode = 1; } u_char ib[4]; while (go) { for (l = 0; l < 4;) { if (*inb64 == '=' || !*inb64) { go = 0; break; } if ((ib[l] = decodetab[*inb64++]) < 128) l++; } if (len < bound) len += decode_true_b64block(ib,out+len,l); else { u_char ob[3]; int lb = decode_true_b64block(ib,ob,l); int lm = olmax-len; if (lm > lb) lm = lb; for (i = 0; i < lm; i++) out[len++] = ob[i]; break; } } if (endinp) *endinp = inb64; return len; } // base64 encode a stream adding padding and line breaks void b64_file_encode( FILE *in, FILE *out, int lsize, char *eol ) { u_char inb[3], outb[4]; int i, len, blocks = 0; if (lsize < 1) lsize = 76; if (!eol) eol = "\n"; while(!feof(in)) { len = 0; for (i = 0; i < 3; i++) { inb[i] = getc (in); if (!feof(in)) { len++; } else { inb[i] = 0; } } if (len) { encode_b64block (inb,outb,len); for (i = 0; i < 4; i++) { putc (outb[i],out); } blocks++; } if (blocks >= (lsize/4) || feof(in)) { if (blocks) fputs (eol,out); blocks = 0; } } } // decode a base64 encoded stream discarding padding, line breaks and noise void b64_file_decode (FILE *in, FILE *out) { u_char buf[4097]; int l; while (fgets((char *)buf,4097,in)) { l = decode_b64str (buf,buf,4097,NULL); fwrite (buf,1,l,out); } } #if 0 main () { // decode (stdin,stdout); u_char x = 0xf1; u_char y = (x>>4); char *in = "Ala"; char out[10]; out[4] = 0; int l = strlen(in); encode_b64block (in,out,l); printf ("%s\n",out); out[4] = ' '; out[9] = 0; l = decode_b64block (out,out+5,4); out[5+l] = 0; printf ("l = %d [%s]\n",l,out); char outp[100]; u_char *last; int ls = decode_b64str (" QWxhZGRpbjpvcGVuIHNlc2FtZQ==",outp,19,&last); outp[ls] = 0; printf ("decode_b64str %d <%s> [%c]\n",ls,outp,*last); char b64[100]; int ic, lb64 = encode_b64 (outp,ls,b64,100,&ic); b64[lb64] = 0; printf ("encode_b64 = %d <%s> %d\n",lb64,b64,ic); } #else main () { char buf[1000]; #if 0 while (fgets(buf,1000,stdin)) { int l = decode_b64str(buf,buf,999,NULL); write (1,buf,l); } #else // b64_file_decode (stdin,stdout); b64_file_encode(stdin,stdout,0,0); #endif } #endif
I hope it will be useful.