2

I've created code designed to bind a new socket to the computer's addresses for listening for incoming connections on a specific port. I'm using getaddrinfo. Is this the best way? It seems pointless converting the port integer to a string. Is there a way to do this with no need for sprintf?

bool CBSocketBind(void * socketID,u_int16_t port){
    struct addrinfo hints,*res,*ptr;
    int socketIDInt;
    // Set hints for the computer's addresses.
    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    // Get host for listening
    char portStr[6];
    sprintf(portStr, "%u",port);
    if (getaddrinfo(NULL, portStr, &hints, &res) != 0)
        return false;
    // Attempt to bind to one of the addresses.
    for(ptr = res; ptr != NULL; ptr = ptr->ai_next) {
        if ((socketIDInt = socket(ptr->ai_family, ptr->ai_socktype,ptr->ai_protocol)) == -1)
            continue;
        if (bind(socketIDInt, ptr->ai_addr, ptr->ai_addrlen) == -1) {
            close(socketIDInt);
            continue;
        }
        break; // Success.
    }
    freeaddrinfo(res);
    if (ptr == NULL) // Failure
        return false;
    socketID = malloc(sizeof(int));
    *(int *)socketID = socketIDInt; // Set socket ID
    // Make socket non-blocking
    fcntl(socketIDInt,F_SETFL,fcntl(socketIDInt,F_GETFL,0) | O_NONBLOCK);
    return true;
}
Matthew Mitchell
  • 5,293
  • 14
  • 70
  • 122

3 Answers3

3

The design of getaddrinfo() was intended to accommodate any network protocol, which may use a different designation for a particular service. The designation may not be numeric, or may use a larger number than the 16-bit integer port numbers used by TCP. For maximal flexibility a service name may be provided, which can be translated to a protocol and any other information specific to the protocol, e.g. ftp → IPv4 or IPv6 TCP port 21. If you know that you will always be using IPv4 or IPv6 and TCP then you can pass NULL for the service name and fill in the port number yourself in the returned socket address structure (sin_port field for IPv4 or sin6_port field for IPv6). However, it is probably cleaner to just use sprintf(); this will handle the different socket address structures used by IPv4 and IPv6, and the requirement to store the port number in network byte order, and may also allow you to support other future protocols without code changes.

mark4o
  • 58,919
  • 18
  • 87
  • 102
2

The reason getaddrinfo() takes a string rather than an integer is that the service doesn't have to be just a port. The user could, for example, ask for service "http".

chrisaycock
  • 36,470
  • 14
  • 88
  • 125
  • OK thanks. If I was designing getaddrinfo I would have provided constants for common protocols such as http instead of using strings. But if this is the idiomatic way of doing it then I'll just have to leave it this way. Not a big problem of-course, I just don't like the way it is done. If anyone has suggestions for improvements than I'll still take a look. – Matthew Mitchell Jul 16 '12 at 18:43
  • @MatthewMitchell The reason "http" isn't a constant is that the admin can add new services as well as change the default port for existing services. See `/etc/services` on your machine. – chrisaycock Jul 16 '12 at 19:07
  • Yes, though the port name could be a signed integer where the services constants are negative numbers which are mapped to the port numbers used by the system. But it doesn't matter. It is the way it is. – Matthew Mitchell Jul 16 '12 at 19:42
  • 1
    You're arguing against something created in the '70s. Did they know about http then? :) – JimR Jul 16 '12 at 19:50
  • @MatthewMitchell The only way to make something like this (1) portal, (2) future-proof, and (3) general-purpose is to **not** use pre-defined constants. What if MatthewMitchellProtocol (MMP) takes off in the future? Anyone without an MMP integer constant in his header file will be screwed. Meanwhile, everyone who had the foresight to simply use string representation will be fine. – chrisaycock Jul 16 '12 at 20:00
  • I was just saying that would have been the way I'd have done it but of-course it's pointless trying to change it now. – Matthew Mitchell Jul 16 '12 at 20:02
1

That looks fairly idiomatic of how getaddrinfo() is normally used in AI_PASSIVE state. The stringified number is typical.

LeoNerd
  • 8,344
  • 1
  • 29
  • 36
  • OK thanks, though if there is a better way of binding a socket for listening for incoming connections (according to anyone) I'm willing to read suggestions. – Matthew Mitchell Jul 16 '12 at 18:44