< IPv4 & IPv6 Server Examples | Internet Protocol Main | Chap 4: Other Supported Protocols >
What do we have in this chapter 3 part 7?
|
The IPv6 Client Program Example
The following program example demonstrates the IPv6 client program with host name as the argument. Take note that we include the inet_pton() source code as a function in this program just to demo its functionalities on Windows system prior to Vista or Server 2008. In actual implementation the function should be called directly or included through the header file.
#include <winsock2.h> #include <Ws2tcpip.h> #include <stdio.h> // for inet_pton() #include <ctype.h> #include <string.h> #include <errno.h>
// For Windows 2008 Server or Vista please // use the Microsoft 'standard' versions available at: // http://msdn.microsoft.com/en-us/library/cc805844(VS.85).aspx // http://msdn.microsoft.com/en-us/library/cc805843(VS.85).aspx // // UNIX/BSD/LINUX: // http://www.opengroup.org/onlinepubs/000095399/functions/inet_ntop.html // //======================================================================== // Just for demo, by changing the function names, we use the source code // directly to provide inet_pton() functionalities taken from: // http://www.koders.com/c/fid74A647B73741DDD374D5478366A00584E8740D43.aspx?s=md5 #ifndef EAFNOSUPPORT # define EAFNOSUPPORT EINVAL #endif
|
#define NS_INADDRSZ 4
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
static int inet_pton4_gedik (const char *src, unsigned char *dst);
static int inet_pton6_gedik (const char *src, unsigned char *dst);
/* int inet_pton(af, src, dst)
* convert from presentation format (which usually means ASCII printable)
* to network format (which is usually some kind of binary format).
* return:
* 1 if the address was valid for the specified address family
* 0 if the address wasn't valid (`dst' is untouched in this case)
* -1 if some other error occurred (`dst' is untouched in this case, too)
* original author: Paul Vixie, 1996.
*/
int inet_pton_gedik (int af, const char *src, void *dst)
{
switch (af)
{
case AF_INET:
return (inet_pton4_gedik (src, dst));
case AF_INET6:
return (inet_pton6_gedik (src, dst));
default:
errno = EAFNOSUPPORT;
return (-1);
}
/* NOTREACHED */
}
/* int inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal, octal (with the
* exception of 0) and shorthand.
* return:
* 1 if `src' is a valid dotted quad, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* original author: Paul Vixie, 1996.
*/
static int inet_pton4_gedik (const char *src, unsigned char *dst)
{
int saw_digit, octets, ch;
unsigned char tmp[NS_INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while ((ch = *src++) != '\0')
{
if (ch >= '0' && ch <= '9')
{
unsigned new = *tp * 10 + (ch - '0');
if (saw_digit && *tp == 0)
return (0);
if (new > 255)
return (0);
*tp = new;
if (!saw_digit)
{
if (++octets > 4)
return (0);
saw_digit = 1;
}
}
else if (ch == '.' && saw_digit)
{
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
}
else
return (0);
}
if (octets < 4)
return (0);
memcpy (dst, tmp, NS_INADDRSZ);
return (1);
}
/* int inet_pton6(src, dst)
* convert presentation level address to network order binary form.
* return:
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
* notice:
* (1) does not touch `dst' unless it's returning 1.
* (2) :: in a full address is silently ignored.
* credit:
* inspired by Mark Andrews.
* original author: Paul Vixie, 1996.
*/
static int inet_pton6_gedik (const char *src, unsigned char *dst)
{
static const char xdigits[ ] = "0123456789abcdef";
unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
const char *curtok;
int ch, saw_xdigit;
unsigned val;
tp = memset (tmp, '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
saw_xdigit = 0;
val = 0;
while ((ch = tolower (*src++)) != '\0')
{
const char *pch;
pch = strchr (xdigits, ch);
if (pch != NULL)
{
val <<= 4;
val |= (pch - xdigits);
if (val > 0xffff)
return (0);
saw_xdigit = 1;
continue;
}
if (ch == ':')
{
curtok = src;
if (!saw_xdigit)
{
if (colonp)
return (0);
colonp = tp;
continue;
}
else if (*src == '\0')
{
return (0);
}
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
saw_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4_gedik (curtok, tp) > 0)
{
tp += NS_INADDRSZ;
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if (saw_xdigit)
{
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
}
if (colonp != NULL)
{
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
if (tp == endp)
return (0);
for (i = 1; i <= n; i++)
{
endp[-i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
memcpy (dst, tmp, NS_IN6ADDRSZ);
return (1);
}
// ========================================================================
// Some constants used by this program
#define BUFFER_LENGTH 256
#define FALSE 0
#define SERVER_NAME "127.0.0.1"
// What is the maximum hostname/FQDN/Server name?
#define NI_MAXHOST 1025
// Pass in 1 parameter which is either the
// address or host name of the server, or
// set the server name in the #define SERVER_NAME
int main(int argc, char *argv[])
{
// Variable and structure declaration an definition
WSADATA wsaData;
int sd = -1, rc, j = 0, bytesReceived = 0;
char buffer[BUFFER_LENGTH];
char server[NI_MAXHOST];
char *servport = "7171";
struct in6_addr serveraddr;
struct addrinfo hints, *res=NULL;
// Initialize the use of the Winsock DLL by a process
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Client: WSAStartup() failed with error code %ld\n", WSAGetLastError());
return 1;
}
else
printf("Client: WSAStartup() looks fine!\n");
// Using do/while(FALSE) loop for 'efficient' error cleanup
do
{
// If an argument was passed in, use this as the server, otherwise
// use the default value set in the #define directive
if (argc > 1)
strcpy_s(server, sizeof(server), argv[1]);
else
strcpy_s(server, sizeof(server), SERVER_NAME);
memset(&hints, 0x00, sizeof(hints));
hints.ai_flags = AI_NUMERICSERV;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
// Check if we were provided the address of the server using
// inet_pton() to convert the text form of the address to binary
// form. If it is numeric then we want to prevent getaddrinfo()
// from doing any name resolution. Here we are using a custom made
// inet_pton() for system < Vista or Windows 2008 server...
// If no error occurs, the inet_pton()/InetPton() function returns
// a value of 1 and the buffer pointed to by the pAddrBuf (&serveraddr) parameter
// contains the binary numeric IP address in network byte order. The InetPton()
// function returns a value of 0 if the pAddrBuf parameter points to a string
// that is not a valid IPv4 dotted-decimal string or a valid IPv6 address string.
// Otherwise, a value of -1 is returned, and a specific error code can be retrieved
// by calling the WSAGetLastError()
rc = inet_pton_gedik(AF_INET, server, &serveraddr);
printf("Client: First rc returned value is %d\n", rc);
if (rc == 1) // valid IPv4 text address?
{
// When the AI_NUMERICHOST bit is set, the nodename (hostname) parameter
// must contain a non-NULL numeric host address string, otherwise the
// EAI_NONAME error is returned. This flag prevents a name resolution
// service from being called.
hints.ai_family = AF_INET;
// If neither AI_CANONNAME nor AI_NUMERICHOST is used,
// the getaddrinfo() function attempts resolution. If a literal
// string is passed getaddrinfo() attempts to convert the string,
// and if a host name is passed the getaddrinfo() function attempts
// to resolve the name to an address or multiple addresses.
hints.ai_flags |= AI_NUMERICHOST;
}
else
{
rc = inet_pton_gedik(AF_INET6, server, &serveraddr);
printf("Client: Second rc returned value is %d\n", rc);
if (rc == 1) // valid IPv6 text address?
{
hints.ai_family = AF_INET6;
hints.ai_flags |= AI_NUMERICHOST;
}
}
// Get the address information for the server re-using getaddrinfo()
rc = getaddrinfo(server, servport, &hints, &res);
if (rc != 0)
{
printf("Client: getaddrinfo() failed with error code %ld\n", WSAGetLastError());
break;
}
printf("Client: getaddrinfo() returns %ld\n", rc);
// The socket() function returns a socket descriptor, which represents
// an endpoint. The statement also identifies the address family,
// socket type, and protocol using the information returned from getaddrinfo()
sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sd == INVALID_SOCKET)
{
printf("Client: socket() failed with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Client: socket() is OK...\n");
// Use the connect() function to establish a connection to the server.
rc = connect(sd, res->ai_addr, res->ai_addrlen);
if (rc == SOCKET_ERROR)
{
// The res is a linked list of addresses found for server
// If the connect() fails to the first one, subsequent addresses
// (if any) in the list can be tried if required
printf("Client: connect() failed with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Client: connect() also OK!\n");
// Send 256 bytes of $'s to the server
memset(buffer, '$', sizeof(buffer));
rc = send(sd, buffer, sizeof(buffer), 0);
if (rc == SOCKET_ERROR)
{
printf("Client: send() failed lol with error code %ld\n", WSAGetLastError());
break;
}
else
printf("Client: send() seems working!\n");
// In this example we know that the server is going to echo with
// the same 256 bytes that we just sent. Since we know that 256
// bytes are going to be sent back to us, we can use the SO_RCVTIMEO
// socket option and then issue a single recv() and retrieve all of the dat
// The use of SO_RCVTIMEO is already illustrated in the server side of this
// example, so we will do something different here. The 256 bytes of the
// data may arrive in separate packets, therefore we will issue recv() over
// and over again until all 256 bytes have arrived
while (bytesReceived < BUFFER_LENGTH)
{
rc = recv(sd, &buffer[bytesReceived], BUFFER_LENGTH - bytesReceived, 0);
if (rc == SOCKET_ERROR)
{
printf("Client: recv() not working with error code %ld\n", WSAGetLastError());
break;
}
else if (rc == 0)
{
printf("Client: The server closed the connection...\n");
break;
}
else
printf("Client: recv() is in progress...\n");
// Increment the number of bytes that have been received so far
bytesReceived += rc;
}
// Just for display!
printf("Client: Received money is - ");
for(j=0; buffer[j] == '$';j++)
printf("%c",buffer[j]);
printf("\n");
} while (FALSE);
// Do the shutdown() for TCP so receive and send
// operation will no longer be allowed
printf("Client: Ready to do the TCP shutdown()...\n");
if(shutdown(sd, SD_BOTH) == SOCKET_ERROR)
printf("Client: shutdown sd socket and the other end failed with error %ld\n", WSAGetLastError());
else
printf("Client: shutdown() on sd socket and the other end looks OK...\n");
// Close down any open socket descriptors
if(closesocket(sd) == 0)
printf("Client: sd socket closed successfully!\n");
else
printf("Client: Failed to close sd socket!\n");
// Terminates the use of the Ws2_32.DLL for all threads (if any)
printf("Client: Doing the WSACleanup()...\n");
if( WSACleanup() == SOCKET_ERROR)
printf("Client: WSACleanup() failed with error code %ld\n", WSAGetLastError());
else
printf("Client: WSACleanup() confirmed OK!\n");
// Free up results returned from getaddrinfo()
printf("Client: Deallocate the allocated resources if any...\n");
if (res != NULL)
freeaddrinfo(res);
return 0;
}
The following screenshot shows a sample client output run when there is no listening server.
------------------------------------------------------------
Firstly we run the previously created IPv6 server program and make sure the machine used to run the program is IPv6 enabled.
Then we run the client using localhost as the host name/server name and this is a default value hardcoded in the program. The client program can be run from the command prompt.
The following screenshot shows the previous server output.
Using IPv4 address will fail because the server is listening on the IPv6 address. You may want to test this client program using IPv4 server created in other program examples in this tutorial.
Then, using the IPv6 for the client should be fine.
< IPv4 & IPv6 Server Examples | Internet Protocol Main | Chap 4: Other Supported Protocols >