2

I am currently writing an FTP server and I need to parse the ip and port of a remote server from an input string buffer in the following format:

xxx,xxx,xxx,xxx,yyy,zzz

where:

  • xxx stands for an ip address octet in decimal
  • yyy is round((remote port number) / 256)
  • zzz is (remote port number) % 256

For example: 127,0,0,1,123,64 means ip = 127.0.0.1 and port = 31552.

I am currently using sscanf to extract the following fields from the input string buffer:

sscanf(str, "%u,%u,%u,%u,%u,%u", ret_ip, &ip[0], &ip[1], &ip[2], &temp1, &temp2) == 6

where:

  • str is the input buffer
  • ret_ip is of type uint32_t
  • ip's are of type uint32_t
  • temp1 and temp2 are of type unsigned short int

Example code:

#include <stdio.h>
#include <netdb.h>
int main(int argc, char *argv[])
{
    uint32_t ip[4];
    unsigned short int temp, temp1;

    if (sscanf("127,0,0,1,142,214", "%u,%u,%u,%u,%u,%u", &ip[0], &ip[1], &ip[2], &ip[3], &temp, &temp1) == 6)
    {
        printf("%u : %u", temp, temp1);
    }

    return (0);
}

My problem is that, for valid string, the value of temp1 is always 0 (zero), i.e. all the other variables are filled according to string except the temp1. I would appreciate any help.

xorspark
  • 15,749
  • 2
  • 29
  • 38
dotSK
  • 35
  • 4

3 Answers3

4

scanf isn't as forgiving of format specifier mismatches as printf is. The specifiers need to match exactly or else you invoke undefined behavior.

For unsigned short use %hu. %u is for unsigned int.

There are no direct format specifiers for types like uint32_t. You need to use a macro from inttypes.h: "%" SCNu32.

All together:

if (sscanf(str, "%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%" SCNu32 ",%hu,%hu", ret_ip, &ip[0], &ip[1], &ip[2], &temp1, &temp2) == 6)
Community
  • 1
  • 1
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
1

The followings are added to this answer compare to the available answers:

  • Extracting the IP address octets as unsigned char and then store them as a single IP address of size uint_32 instead of having an array of uint_32. See this post for more information.
  • Validating against the sscanf output.
  • The %hu scan code is used for reading unsigned short and the %hhu scan code is used for reading unsigned char.
  • Verifying the process of IP address conversion from string to unitt_32 using inet_pton and from unitt_32 to string using inet_ntop. Read this section of Beej's networking book if you want to learn more.

and here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]){
    unsigned char ip_octects[4] = {0, 0, 0, 0};
    uint32_t ip = 0;
    unsigned short r1 = 0, r2 = 0;
    unsigned char *str_c = "127,0,0,1,142,214";

    if(sscanf(str_c, "%hhu,%hhu,%hhu,%hhu,%hu,%hu", &ip_octects[0],
                    &ip_octects[1], &ip_octects[2], &ip_octects[3], &r1, &r2) == 6){

        printf("Extracted ip : port: %hhu.%hhu.%hhu.%hhu:%hu:%hu\n",
                ip_octects[0], ip_octects[1], ip_octects[2], ip_octects[3], r1, r2);

        ip = ip_octects[0]       | ip_octects[1] << 8 |
             ip_octects[2] << 16 | ip_octects[3] << 24;
        printf("unit32_t  ip  value: %zu\n", ip);

        /* We're done but lets verify the results using inet_pton() and inet_ntop() */
        unsigned char *str_d = "127.0.0.1";
        char str[INET_ADDRSTRLEN];
        struct sockaddr_in sa;
        if(inet_pton(AF_INET, str_d, &(sa.sin_addr)) < 1){
            perror("error: invalid input for inet_pton"); exit(1);
        }
        printf("inet_pton ip  value: %zu\n",sa.sin_addr);
        if(inet_ntop(AF_INET, &(sa.sin_addr), str, INET_ADDRSTRLEN) == NULL){
            perror("error: invalid input for inet_ntop"); exit(1);
        }
        printf("inet_ntop str value: %s\n", str);
    }
    else{
        perror("error: invalid input for sscanf"); exit(1);
    }
    return (0);
}
Community
  • 1
  • 1
hmofrad
  • 1,784
  • 2
  • 22
  • 28
-1

Followling is my code which seems to work and print correct results.

  char sentence []="127,0,0,1,123,64";
  uint16_t ret_ip;
  uint16_t ip1, ip2, ip3;
  uint16_t temp1, temp2;

  sscanf(sentence, "%d,%d,%d,%d,%d,%d", &ret_ip, &ip1, &ip2, &ip3, &temp1, &temp2);

  printf("%d, %d\n", temp1, temp2);
learner
  • 191
  • 1
  • 2
  • 12