I have two strings ip1 = "192.168.145.123" and ip2 = "172.167.234.120".
I can compare these two strings for equality:
strncmp(ip1,ip2) == 0
However how can I find out
if (ip1 > ip2) {
...
}
What I have tried
I can use sscanf:
sscanf(ip1,"%d.%d.%d.%d",&s1,&s2,&s3,&s4)
and store the numbers and compare.
However in 32 bit I can't store the numbers as integers due to the upper limit.
Thus I have no choice but to compare the integers as strings.
Is it worth mentionning that there is also inet_aton ?
You can find the man page here, below is a short description and a short synopsis.
This solution will work on most POSIX systems, but I'm sure there is some equivalent in the Windows APIs, and even some abstraction wrapper.
inet_ntoa() is specified in POSIX.1-2001. inet_aton() is not specified in POSIX.1-2001, but is available on most systems.
Linux Programmer's Manual
inet_aton() converts the Internet host address cp from the IPv4 numbers-and-dots notation into binary form (in network byte order) and stores it in the structure that inp points to.
SYNOPSIS
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
EXAMPLE
An example of the use of inet_aton() and inet_ntoa() is shown below. Here are some example runs:
$ ./a.out 226.000.000.037 # Last byte is in octal
226.0.0.31
$ ./a.out 0x7f.1 # First byte is in hex
127.0.0.1
Program source
#define _BSD_SOURCE
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
struct in_addr addr;
if (argc != 2) {
fprintf(stderr, "%s <dotted-address>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (inet_aton(argv[1], &addr) == 0) {
fprintf(stderr, "Invalid address\n");
exit(EXIT_FAILURE);
}
printf("%s\n", inet_ntoa(addr));
exit(EXIT_SUCCESS);
}
Further informations
Byte ordering (#Jonathan Leffler)
The inet_ntoa() function converts the Internet host address in, given in network byte order, to a string in IPv4 dotted-decimal notation.
inet_aton() converts the Internet host address cp from the IPv4 numbers-and-dots notation into binary form (in network byte order) and stores it in the structure that inp points to.
Structure of in_addr (#POW)
The structure in_addr as used in inet_ntoa(), inet_makeaddr(), inet_lnaof() and inet_netof() is defined in as:
typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr;
};
Compare to address independently of computer-endianness
Addresses in in_addr are in network byte order (big-endian), so as pointed by #glglgl, you have to use ntohl, whose man page is available here.
The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.
uint32_t ntohl(uint32_t netlong);
You can try the sexy way, store all values in one unsigned integer and compare it.
const char* ip1 = "192.168.145.123";
const char* ip2 = "172.167.234.120";
unsigned char s1, s2, s3, s4;
unsigned int uip1, uip2;
sscanf(ip1,"%hhu.%hhu.%hhu.%hhu",&s1,&s2,&s3,&s4);
uip1 = (s1<<24) | (s2<<16) | (s3<<8) | s4; //store all values in 32bits unsigned int
sscanf(ip2,"%hhu.%hhu.%hhu.%hhu",&s1,&s2,&s3,&s4);
uip2 = (s1<<24) | (s2<<16) | (s3<<8) | s4;
if (uip1 > uip2)
{
printf("ip1 greater !");
}
else
{
printf("ip2 greater or equal !");
}
How about this :-
#include<stdio.h>
#include<conio.h>
unsigned int convIP(const char ip[]) {
unsigned char s1, s2, s3, s4;
if (sscanf(ip, "%hhu.%hhu.%hhu.%hhu", &s1, &s2, &s3, &s4) != 4)
return 0;
/* Create a 32 bit Integer using left shift & bitwise OR
MSB LSB
+-----8----+-----8------+-----8-----+----8-----+
| s1 | s2 | s3 | s4 |
+----------+------------+-----------+----------+
*/
return (s1 << 24) | (s2 << 16) | (s3 << 8) | (s4 << 0);
}
int ipComp(const char ip1[], const char ip2[]) {
unsigned int ip_addr1 = convIP(ip1);
unsigned int ip_addr2 = convIP(ip2);
return (ip_addr1 >= ip_addr2);
}
int main()
{
printf("%d\n",ipComp("192.168.145.123","172.167.234.120") ); //1
printf("%d\n", ipComp("10.0.0.1","192.168.1.1") ); //0
printf("%d\n",ipComp("192.168.145.123","192.168.145.123")); //1
}
Edit: As suggested by H2CO3:
You should generally avoid using sscanf, instead you can use strtol(), like this:
unsigned long ip2int(const char *ip)
{
const char *end = ip + strlen(ip);
unsigned long n = 0;
while (ip < end) {
n <<= 8;
n |= strtoul(ip, (char **)&ip, 10);
ip++;
}
return n;
}
A pedantic "after the accepted answer" answer. Error checking employed.
#include <inttypes.h>
int IPstringToUns32(const char *IPString, uint32_t *IPNumber) {
uint8_t c[4]; // LSByte in c[0]
char ch;
const char * format = "%" SCNu8 ".%" SCNu8 ".%" SCNu8 ".%" SCNu8 "%c";
if (4 != sscanf(IPString, format, &c[3], &c[2], &c[1], &c[0], &ch)) {
return 1; // parse error
}
*IPNumber = (((uint32_t) c[3]) << 24) | (((uint32_t) c[2]) << 16)
| (((uint32_t) c[1]) << 8) | ((uint32_t) c[0]);
return 0;
}
Suppose one could instead use uint_fast32_t. This solution allows for leading white space before digits.
[Edit] Added classic %c at the end of the format. Thanks to #glglgl.
Related
Here is the code
#include <stdio.h>
//#include "ConvertEndianess.h"
typedef unsigned char uint8_t;
void ConvertEndianess(uint8_t* buffAddr, uint8_t length);
void ConvertEndianess(uint8_t *buffAddr, uint8_t length)
{
uint8_t i;
uint8_t data;
for (i = 0U; i < (length / 2U); i++)
{
data = buffAddr[i];
buffAddr[i] = buffAddr[(length - 1U) - i];
buffAddr[(length - 1U) - i] = data;
}
}
int main(int argc, char *argv[]){
printf("\nNumber Of Arguments Passed: %d\n",argc);
if(argc < 3){
printf("Too Few Arguments\n");
}
else if(argc > 3){
printf("Too Many Arguments\n");
}
else{
printf("data indianess: %s\n",argv[1]);
ConvertEndianess((uint8_t *)&argv[1], (uint8_t)(*argv[2]));
printf("data indianess: %s\n",argv[1]);
}
}
Here is my argument:
./a 11223344 4
It is printing correctly before going to conversion API but maybe some casting mistake is done by me and I'm not able to identify it.
Can anyone please correct my mistake with explanation?
(uint8_t)(*argv[2])
argv[2] is a char * and according to how your program operates, I assume that is supposed to contain the size of the buffer. Since this is a string (char *), and you want to convert it to a number, you'll need to use sscanf to get this value:
Sample useage:
size_t len;
sscanf(str, "%zu", &len);
Then pass len as the second argument of your conversion function.
The way you are doing it presently passes the ASCII code for the first character of argv[2]. So if the user said the size is "7" you are actually passing the integer 55 to the conversion function.
As a side note, use size_t to represent size, not uint8_t.
The main problem on your code is that you cast &argv[...] with an incompatible type.
argv[...] is a char* so &argv[...] is some char**, and you cast it as a uint8_t* which is incompatible.
Some remarks:
You should not type & before argv[...]:
You can use strlen() instead of passing an argument
You can use strtol() to convert a string into an integer
Indianness or Endianness?
#include <stdio.h>
#include <string.h> /* strlen */
#include <stdlib.h> /* strtol */
typedef unsigned char uint8_t;
void ConvertEndianess(uint8_t *buffAddr, uint8_t length)
{
uint8_t i;
uint8_t data;
for (i = 0U; i < (length / 2U); i++)
{
data = buffAddr[i];
buffAddr[i] = buffAddr[(length - 1U) - i];
buffAddr[(length - 1U) - i] = data;
}
}
int main(int argc, char *argv[]){
printf("\nNumber Of Arguments Passed: %d\n",argc);
if(argc == 2){
/* exemple with strlen */
printf("data Endianess: %s\n",argv[1]);
ConvertEndianess(argv[1], strlen(argv[1]));
printf("data Endianess: %s\n",argv[1]);
}
else if(argc == 3){
/* exemple with strtol */
printf("data Endianess: %s\n",argv[1]);
ConvertEndianess((uint8_t *)argv[1], strtol(argv[2], NULL, 0));
printf("data Endianess: %s\n",argv[1]);
}
}
Is there a function to convert an IPv4 address in dotted decimal notation (e.g., "192.168.0.1") into a decimal value?
There are several questions similar to this, but all are looking for or have answers that involve writing a function to solve the answer. I'm looking for an existing function in a library.
The C standard library doesn't have such a function. It's simple enough that this shouldn't be a problem. Here's some example code using strtoul() for parsing:
int parseIpv4Address(uint32_t *addr, const char *addrStr)
{
const char *p = addrStr;
char *endp;
unsigned long a = strtoul(p, &endp, 10);
if (*endp != '.') return -1;
unsigned long b = strtoul(p = endp+1, &endp, 10);
if (*endp != '.') return -1;
unsigned long c = strtoul(p = endp+1, &endp, 10);
if (*endp != '.') return -1;
unsigned long d = strtoul(p = endp+1, &endp, 10);
if (*endp) return -1;
*addr = (uint32_t) ((a << 24) | (b << 16) | (c << 8) | d);
return 0;
}
as unsigned long must have at least 32 value bits, there's no special care (casting) needed for the shifts here.
It doesn't validate the range of the individual parts, if you need this, you should be able to add it without problems.
You could use something like inet_pton which is pretty straight forward to use as you can see from the example code below.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(void)
{
struct in_addr result;
if(inet_pton(AF_INET,"127.0.0.1",&result))
{
printf("%08x\n",result.s_addr);
}
else
{
printf("Failed\n");
}
return(0);
}
The number is in network byte order, but if you want it in host byte order, you can use ntohl to convert it.
It turns out that Winsock 2 provides the inet_addr() function, which simply returns the equivalent decimal value for the address.
#include <stdio.h>
#include <winsock2.h>
int main(void)
{
char* pAddr = "192.168.0.1";
unsigned long value = inet_addr(pAddr);
if (value == INADDR_NONE)
printf("Invalid format of IP address");
else
printf("Decimal representation of %s is: %lu", pAddr, value);
return 0;
}
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.
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)
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);
}
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);
This program shows correctly the voltages but not specify the direction on the Hyperterminal. It keeps showing N for all the values of voltages.
I've been trying but still couldn't find the bug.
Here's my code down below.
// Using microcontroller AT89C51, ADC 0804 for display on hyperterminal
#include <reg51.h>
#include <string.h>
#define input P1
void delay(unsigned int time);
void SerTx(unsigned char x);
void SerTx_str(unsigned char msg[]);
void SerInit();
void convert(unsigned int p);
unsigned char msg[] ="Voltage= ";
sbit wint =P3^4;
void main()
{
unsigned int y,z,p;
int d1,d2,d3;
unsigned char buffer[10];
SerInit();
input=0xff;
while(1)
{
wint=0;
delay(1);
wint=1;
delay(5);
wint=0;
delay(10);
z=input;
y=z*2;
d1=y%10;
y=y/10;
d2=y%10;
d3=y/10;
buffer[0] =d3+48;
buffer[1] =d2+48;
buffer[2] =d1+48;
SerTx_str(msg);
SerTx_str(buffer);
SerTx_str(" ");
p=buffer;
convert(p);
SerTx(13);
SerTx(13);
delay(20);
SerTx(10);
}
}
p = buffer;
Now p holds the address of buffer (because buffer is an array, this is short for p = &buffer[0];)
convert(p);
Now you convert the value in p - that is, you convert the address of buffer.
I'm guessing you want to convert the same number you're displaying? The number you're displaying is z*2, so just use:
convert(z*2);
I am not receiving anything in buffer. Wherever I printf my buffer, it is always empty or shows garbage value. Can anyone help?
I defined header, packet and called them in my main, but buffer still shows garbage.
#include <stdint.h>
struct header {
uint16_t f1;
uint16_t f2;
uint32_t f3;
};
struct data {
uint16_t pf1;
uint64_t pf2;
};
#include <arpa/inet.h>
#include <string.h>
#include <stdint.h>
#include "packet.h"
void htonHeader(struct header h, char buffer[8]) {
uint16_t u16;
uint32_t u32;
u16 = htons(h.f1);
memcpy(buffer+0, &u16, 2);
printf("Value of buff is: %hu\n",buffer);
u16 = htons(h.f2);
memcpy(buffer+2, &u16, 2);
u32 = htonl(h.f3);
memcpy(buffer+4, &u32, 4);
}
void htonData(struct data d, char buffer[10]) {
uint16_t u16;
uint32_t u32;
u16 = htons(d.pf1);
memcpy(buffer+0, &u16, 2);
u32 = htonl(d.pf2>>32);
memcpy(buffer+2, &u32, 4);
u32 = htonl(d.pf2);
memcpy(buffer+6,&u32, 4);
}
void HeaderData(struct header h, struct data d, char buffer[18]) {
htonHeader(h, buffer+0);
htonData(d, buffer+8);
printf("buff is: %s\n",buffer);
}
#include <stdio.h>
#include "packet.c"
#include <string.h>
#include<stdlib.h>
int main(){
struct header h;
struct data d;
char buff[18];
//printf("Packet is: %s\n",buff);
printf("Generating Packets..... \n");
h.f1=1;
d.pf1=2;
h.f2=3;
d.pf2=4;
h.f3=5;
HeaderData(h,d,buff);
strcat(buff,buff+8);
printf("Packet is: %s\n",buff);
return 0;
}
The problem is that your printf()s are either syntactically wrong (printf( "%hu", ... ); expects an unsigned short as parameter, but you pass a pointer) or you try to print buff by using "%s" but the content is binary, not text. What you could do instead was doing some kind of hexdump, like:
int i;
for( i=0; i<sizeof( buff ); i++ ) {
printf( "%x ", buff[i] & 0xff );
}
puts( "" ); // terminate the line
Please note, that using sizeof works im main() only, in the other function you've got to determine the buffer size differently.
Besides: because of the binary content of buff, you can't use strcat(). Even if you have made sure that there is a '\0' behind the last value you have copied (I haven't checked if you have), depending on the integer values you copy, there may be another '\0' value before that one and strcat() would overwrite everything form that point on.