Linux Socket Programming by Example - Warren Gay

< BACKCONTINUE >
159231153036212041242100244042145096184016146223183074028121223009001094106148107176157205189

Obtaining the Socket Address

If a C library function that you wrote receives a socket as an input argument, then you will not know what the socket address of that socket is. This is because your function did not create the socket; and, unless the socket address is also passed to your function as input, you will not know what the address is. The function getsockname(2) permits your function to obtain it.

The function synopsis for getsockname(2) is as follows:

					
#include <sys/socket.h>

int getsockname(int s, struct sockaddr *name, socklen_t *namelen)
				

This function takes the following three input arguments:

  1. The socket s to query for the socket address.

  2. The pointer to the receiving buffer (argument name).

  3. Pointer to the maximum length variable. This variable provides the maximum length in bytes that can be received in the buffer (argument namelen). This value is updated with the actual number of bytes written to the receiving buffer.

Note that like the bind(2) function, getsockname(2) uses the generic address structure sockaddr because it can be used for any type of socket address. This will mean that you will likely need to apply the C language casting operator on the pointer supplied in this argument.

The length argument namelen specifies the maximum number of bytes that can be received into argument two (name). Prior to returning to the caller, however, the value of namelen is overwritten to indicate how many bytes were actually returned in the receiving buffer. This will be less than or equal to the original value supplied.

CAUTION

Never supply the address of a constant for the socket address length in a call to getsockname(2). This should not be done because the length variable is updated with the actual number of bytes placed into the receiving address structure.

If you do supply the address of a constant, the value of the constant will be overwritten. This will cause havoc in your program. On some CPU platforms, you might experience a program fault instead.


The function returns zero if it is successful. If an error occurs, the return value -1 is returned, and the reason for the error is posted to the variable errno.

Writing a sock_addr() Function

To illustrate the use of getsockaddr(2), a small function has been presented in Listing 5.2, which accepts as input a socket descriptor. The function obtains the socket's address by calling getsockaddr(2), and then formats a string to be returned to the caller which can be used in a printf(3) call.

Example 5.2. sckname.c—The getsockaddr(2) Function Call
 <$nopage>
001 1:   /* sckname.c:
002 2:    *
003 3:    * Demonstrate getsockname(2):
004 4:    */
005 5:   #include <stdio.h>
006 6:   #include <unistd.h>
007 7:   #include <stdlib.h>
008 8:   #include <errno.h>
009 9:   #include <string.h>
010 10:  #include <sys/types.h>
011 11:  #include <sys/stat.h>
012 12:  #include <sys/socket.h>
013 13:  #include <netinet/in.h>
014 14:  #include <arpa/inet.h>
015 15:
016 16:  /*
017 17:   * This saves lines of code later:
018 18:   */
019 19:  static void
020 20:  bail(const char *on_what) {
021 21:      perror(on_what);     /* Report error */
022 22:      exit(1);            /* Exit Program */
023 23:  }
024 24:
025 25:  /*
026 26:   * This function accepts as input a socket
027 27:   * for which a socket address must be
028 28:   * determined for it. Then the address
029 29:   * is converted into a string and returned.
030 30:   *
031 31:   * If an error occurs, NULL is returned.
032 32:   */
033 33:  char *
034 34:  sock_addr(int s,char *buf,size_t bufsiz) {
035 35:      int z;            /* Status return code */
036 36:      struct sockaddr_in adr_inet;/* AF_INET */
037 37:      int len_inet;                /* length  */
038 38:
039 39:      /*
040 40:       * Obtain the address of the socket:
041 41:       */
042 42:      len_inet = sizeof adr_inet;
043 43:
044 44:      z = getsockname(s,
045 45:          (struct sockaddr *)&adr_inet,
046 46:          &len_inet);
047 47:
048 48:      if ( z == -1 )
049 49:          return NULL;    /* Failed */
050 50:
051 51:      /*
052 52:       * Convert address into a string
053 53:       * form that can be displayed:
054 54:       */
055 55:      snprintf(buf,bufsiz,
056 56:          "%s:%u",
057 57:          inet_ntoa(adr_inet.sin_addr),
058 58:          (unsigned)ntohs(adr_inet.sin_port));
059 59:
060 60:      return buf;
061 61:  }
062 62:
063 63:  /*
064 64:   * Main Program:
065 65:   */
066 66:  int
067 67:  main(int argc,char **argv,char **envp) {
068 68:      int z;            /* Status return code */
069 69:      int sck_inet;                /* Socket  */
070 70:      struct sockaddr_in adr_inet;/* AF_INET */
071 71:      int len_inet;                /* length  */ <$nopage>
072 72:      char buf[64];             /* Work buffer */
073 73:
074 74:      /*
075 75:       * Create an IPv4 Internet Socket:
076 76:       */
077 77:      sck_inet = socket(AF_INET,SOCK_STREAM,0);
078 78:
079 79:      if ( sck_inet == -1 )
080 80:          bail("socket()");
081 81:
082 82:      /*
083 83:       * Create an AF_INET address:
084 84:       */
085 85:      memset(&adr_inet,0,sizeof adr_inet);
086 86:      adr_inet.sin_family = AF_INET;
087 87:      adr_inet.sin_port = htons(9000);
088 88:      inet_aton("127.0.0.24",&adr_inet.sin_addr);
089 89:      len_inet = sizeof adr_inet;
090 90:
091 91:      /*
092 92:       * Now bind the address to the socket:
093 93:       */
094 94:      z = bind(sck_inet,
095 95:          (struct sockaddr *)&adr_inet,
096 96:          len_inet);
097 97:      if ( z == -1 )
098 98:          bail("bind()");
099 99:
100 100:     /*
101 101:      * Now test our sock_addr() function:
102 102:      */
103 103:     if ( !sock_addr(sck_inet,buf,sizeof buf) )
104 104:         bail("sock_addr()");
105 105:
106 106:     printf("Address is '%s'\n",buf);
107 107:
108 108:     close(sck_inet);
109 109:     return 0;
110 110: }

Listing 5.2 shows how getsockname(2) could be used by a library C function. Much of the main() program is review for you, because it is very similar to the main() program in Listing 5.1. The general steps used in the main() program are

  1. Declarations for the socket sck_inet, the address adr_inet, and the address length len_inet are given in lines 69 to 71.

  2. A character array buffer buf[64] is declared in line 72 for use later in the program.

  3. The socket is created by socket(2) in line 77.

  4. A socket address is established in lines 85 to 95 with the use of the bind(2) function call.

  5. The sock_addr() function is called inside the if statement in line 103. If the function returns a null pointer, the statement in line 104 is executed and the program will terminate.

  6. If Step 5 succeeds, however, the buffer buf[] will contain a string that represents the socket address for socket sck_inet. The printf(3) call in line 106 reports this result.

In short, the main program does two things:

  • Creates a socket and establishes an address for it.

  • Calls the function sock_addr() to see whether it can find out what the socket's address is.

Now, examine the steps used by the sock_addr() function that was shown in Listing 5.2:

  1. The function declaration for sock_addr() starts in line 33. It accepts as input the socket s for which the function must determine the address. The printable socket address is returned in the supplied buffer buf, which is a maximum size of bufsiz bytes.

  2. Declarations for temporary values adr_inet and len_inet are declared in lines 36 and 37.

  3. The maximum length is established in variable len_inet before calling getsockname(2). This establishes the maximum number of bytes of address information that can be returned.

  4. The getsockname(2) function is called in line 44. If successful, z will be set to zero, and the address will be loaded into the structure adr_inet. The length variable len_inet will be overwritten with the actual size used.

  5. Check for errors in line 48. When the return value is -1, an error has occurred. The function returns a null value to indicate that an error has occurred. The caller can test errno for the cause of the error.

  6. Lines 55 to 58 format a string into the caller's buffer buf of maximum length bufsiz. The value of bufsiz must include the terminating null byte.

  7. The pointer to the caller's buffer is returned in line 60 to indicate that the call was successful.

The function sock_addr() not only determines the socket address for the caller, but formats it nicely into a string that can be used in a printf(3) call.

TIP

Did you notice the use of the snprintf(3) call instead of the more traditional sprintf(3) function? The newer snprintf(3) call is able to limit its formatting to the maximum size of the buffer it is formatting to. This is extremely important for software integrity, and you should use the snprintf(3) instead, whenever possible.


The following shows how to compile the program in Listing 5.2:

						
$ make sckname
gcc -c  -D_GNU_SOURCE -Wall sckname.c
gcc sckname.o -o sckname
					

After the program is compiled, you can invoke it to produce its output as follows:

						
$ ./sckname
Address is '127.0.0.24:9000'
$
					

Looking at the line of output where the address is reported as '127.0.0.24:9000', you can see that the function sock_addr() was successful at performing its mission. This was the address that was established in the main() program in lines 87 and 88.

Obtaining a Peer Socket Address

In the last section, you saw that the function getsockname(2) is quite useful at obtaining a socket's address. However, there will be times when your code will want to determine the remote address that your socket is connected to. Determining the remote address of a socket is like finding out the caller's telephone number when they have called you—similar to North America's CallerID.

The function to do this is called getpeername(2). This function will be useful to you when you start examining and writing server code. It is introduced here because it is so similar to getsockname(2). The function synopsis for getpeername(2) is as follows:

						
#include <sys/socket.h>

int getpeername(int s, struct sockaddr *name, socklen_t *namelen);
					

You can see that the function arguments are identical to the getsock name(2) function. For completeness, the arguments are described again as follows:

  1. The socket s to query for the socket address.

  2. The pointer to the receiving buffer (argument name).

  3. Pointer to the maximum length variable. This variable provides the maximum length in bytes that can be received in the buffer (argument namelen). This value is updated with the actual number of bytes written to the receiving buffer.

The function returns zero if the operation succeeds. If an error occurs, the value -1 is returned and the value errno will contain the reason for the error.

Listing 5.3 shows some code that defines a function named peer_addr(). This code is very similar in design to the sock_addr() function of Listing 5.2. This is not a complete example, however, because it shows the source code only for the function itself (it lacks a main program). This code can be revisited later in this book, when connection-oriented communications are discussed. For now, simply appreciate it as an example of how the function getpeername(2) can be used.

Example 5.3. getpeer.c&mdash;The getpeername(2) Function
 <$nopage>
001 1:   /* getpeer.c:
002 2:    *
003 3:    * Demonstrate getpeername(2):
004 4:    */
005 5:   #include <stdio.h>
006 6:   #include <unistd.h>
007 7:   #include <stdlib.h>
008 8:   #include <errno.h>
009 9:   #include <string.h>
010 10:  #include <sys/types.h>
011 11:  #include <sys/stat.h>
012 12:  #include <sys/socket.h>
013 13:  #include <netinet/in.h>
014 14:  #include <arpa/inet.h>
015 15:
016 16:  /*
017 17:   * This function accepts as input a socket
018 18:   * for which a peer socket address must be
019 19:   * determined for it. Then the address
020 20:   * is converted into a string and returned.
021 21:   *
022 22:   * If an error occurs, NULL is returned.
023 23:   */
024 24:  char *
025 25:  peer_addr(int s,char *buf,size_t bufsiz) {
026 26:      int z;            /* Status return code */
027 27:      struct sockaddr_in adr_inet;/* AF_INET */
028 28:      int len_inet;                 /* length  */
029 29:
030 30:      /*
031 31:       * Obtain the address of the socket:
032 32:       */
033 33:      len_inet = sizeof adr_inet;
034 34:
035 35:      z = getpeername(s,
036 36:          (struct sockaddr *)&adr_inet,
037 37:          &len_inet);
038 38:
039 39:      if ( z == -1 )
040 40:          return NULL;    /* Failed */
041 41:
042 42:      /*
043 43:       * Convert address into a string
044 44:       * form that can be displayed:
045 45:       */
046 46:      z = snprintf(buf,bufsiz,
047 47:          "%s:%u",
048 48:          inet_ntoa(adr_inet.sin_addr),
049 49:          (unsigned)ntohs(adr_inet.sin_port));
050 50:
051 51:      if ( z == -1 )
052 52:          return NULL;    /* Buffer too small */
053 53:
054 54:      return buf;
055 55:  }

The steps used in peer_name() are the same basic steps as sock_addr(). However, one small improvement was made to the program. Let's take a look at the steps:

  1. The function declaration for sock_addr() starts in line 24. It accepts as input the socket s for which the function must determine the peer address. The printable socket address is returned in the supplied buffer buf, which is a maximum size of bufsiz bytes.

  2. Declarations for temporary values adr_inet and len_inet are declared in lines 27 and 28.

  3. The maximum length is established in variable len_inet before calling getsockname(2) (line 33). This establishes the maximum number of bytes of address information that can be returned.

  4. The getpeername(2) function is called in line 35. If successful, z will be set to zero, and the address will be loaded into the structure adr_inet. The length variable len_inet will be overwritten with the actual size used.

  5. Check for errors in line 39. When the return value is -1, an error has occurred. The function returns a null value to indicate that an error has occurred. The caller can test errno for the cause of the error.

  6. Lines 46 to 49 format a string into the caller's buffer buf of maximum length bufsiz. The value of bufsiz must include the terminating null byte.

  7. A small improvement to the program was added at lines 51 and 52. The snprintf(3) function returns -1 if the formatted result is too large to fit into the user's buffer buf of maximum length bufsiz. If this should happen, the program returns a null pointer in line 52 to indicate that the peer_addr() function was not fully successful.

  8. The pointer to the caller's buffer is returned in line 54 to indicate that the call was successful.

If you skimmed over the steps described, the improvement that was made to the program is described in step 7.

< BACKCONTINUE >

Index terms contained in this section

addresses
      obtaining 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th
      peer sockets 2nd 3rd 4th
arguments
      getpeername(2) function
      getsockname(2) function
examples
      getpeername (2) function 2nd 3rd
      getsockaddr (2) function 2nd 3rd
functions
      getpeername(2)
      getsockname(2) 2nd 3rd
      sock addr
      sock addr ()
getpeername(2) function
      arguments
      example 2nd 3rd
getsockaddr (2) function
      examples 2nd 3rd
getsockname(2) function 2nd 3rd
      arguments
obtaining
      socket addresses 2nd 3rd 4th 5th 6th 7th 8th 9th
            peer 2nd 3rd 4th
peer sockets
      addresses 2nd 3rd 4th
sock addr() function
      writing
sockets
     addresses
            obtaining 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th
writing
      sock addr() function