Calculate broadcast address from ip and subnet mask - c

I want to calculate the broadcast address for:
IP: 192.168.3.1
Subnet: 255.255.255.0
= 192.168.3.255
in C.
I know the way (doing fancy bitwise OR's between the inversed IP and subnet), but my problem is I come from the green fields of MacOSX Cocoa programing.
I looked into the source of ipcal, but wasn't able to integrate it into my code base. There must be a simple ten lines of code somewhere on the internet, I just can't find it.
Could someone point me to a short code example of how to do it in C?

Just calculate:
broadcast = ip | ( ~ subnet )
(Broadcast = ip-addr or the inverted subnet-mask)
The broadcast address has a 1 bit where the subnet mask has a 0 bit.

I understand that the OP had at least a vague understanding of the bit-level arithmetic but was lost on converting the strings to numbers and its inverse. here's a working (with minimal testing anyway) example, using froh42's calculation.
jcomeau#aspire:~/rentacoder/jcomeau/freifunk$ cat inet.c; make inet; ./inet 192.168.3.1 255.255.255.0
#include <arpa/inet.h>
#include <stdio.h>
int main(int argc, char **argv) {
char *host_ip = argc > 1 ? argv[1] : "127.0.0.1";
char *netmask = argc > 2 ? argv[2] : "255.255.255.255";
struct in_addr host, mask, broadcast;
char broadcast_address[INET_ADDRSTRLEN];
if (inet_pton(AF_INET, host_ip, &host) == 1 &&
inet_pton(AF_INET, netmask, &mask) == 1)
broadcast.s_addr = host.s_addr | ~mask.s_addr;
else {
fprintf(stderr, "Failed converting strings to numbers\n");
return 1;
}
if (inet_ntop(AF_INET, &broadcast, broadcast_address, INET_ADDRSTRLEN) != NULL)
printf("Broadcast address of %s with netmask %s is %s\n",
host_ip, netmask, broadcast_address);
else {
fprintf(stderr, "Failed converting number to string\n");
return 1;
}
return 0;
}
cc inet.c -o inet
Broadcast address of 192.168.3.1 with netmask 255.255.255.0 is 192.168.3.255

Could it be?
unsigned broadcast(unsigned ip,unsigned subnet){
unsigned int bits = subnet ^ 0xffffffff;
unsigned int bcast = ip | bits;
return bcast;
}
Edit: I considered that both ip and subnet are without "."

Here is how to do it in C#. for example using ip 10.28.40.149 with netmask 255.255.252.0 returns 10.28.43.255 which is the correct broadcast address. thanks to some code from here
private static string GetBroadcastAddress(string ipAddress, string subnetMask) {
//determines a broadcast address from an ip and subnet
var ip = IPAddress.Parse(ipAddress);
var mask = IPAddress.Parse(subnetMask);
byte[] ipAdressBytes = ip.GetAddressBytes();
byte[] subnetMaskBytes = mask.GetAddressBytes();
if (ipAdressBytes.Length != subnetMaskBytes.Length)
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
for (int i = 0; i < broadcastAddress.Length; i++) {
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
}
return new IPAddress(broadcastAddress).ToString();
}

ok whom will look for this code in the future. I have spend sometimes today as I needed this, here is the full code and it works :) simply copy and paste it and then import the required dlls.
private IPAddress CalculateBroadCastAddress(IPAddress currentIP, IPAddress ipNetMask)
{
string[] strCurrentIP = currentIP.ToString().Split('.');
string[] strIPNetMask = ipNetMask.ToString().Split('.');
ArrayList arBroadCast = new ArrayList();
for (int i = 0; i < 4; i++)
{
int nrBCOct = int.Parse(strCurrentIP[i]) | (int.Parse(strIPNetMask[i]) ^ 255);
arBroadCast.Add(nrBCOct.ToString());
}
return IPAddress.Parse(arBroadCast[0] + "." + arBroadCast[1] +
"." + arBroadCast[2] + "." + arBroadCast[3]);
}
private IPAddress getIP()
{
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip;
}
}
return null;
}
private IPAddress getSubnetMask()
{
NetworkInterface[] Interfaces = NetworkInterface.GetAllNetworkInterfaces();
IPAddress ip = getIP();
foreach (NetworkInterface interf in Interfaces)
{
UnicastIPAddressInformationCollection UnicastIPInfoCol = interf.GetIPProperties().UnicastAddresses;
foreach (UnicastIPAddressInformation UnicatIPInfo in UnicastIPInfoCol)
{
if (UnicatIPInfo.Address.Equals(ip))
return UnicatIPInfo.IPv4Mask;
}
}
return null;
}
Then just call it like :
IPAddress broadcastip = CalculateBroadCastAddress(getIP(), getSubnetMask());
Happy coding :)

Related

Working with byte array I get an "invalid conversion from char* to byte"

I am reading and writing to an RFID tag using MFRC522.h
I can currently read the UID of a card and dump it to "UIDChar"
The UID of a card typically is 8 characters.
UID Example: 467EE9A9
I can use the mfrc522.MIFARE_SetUid function to write this UID to a new card. In order to do this I have to set the newUID to:
0x46,0x7E,0xE9,0xA9f
I have written this into my code.
What I am wanting to do is convert the UID string into a byte array so that I can use that in place of my manually written 0x46,0x7E,0xE9,0xA9.
I use the convert function to convert the UID into that format.
It can that be displayed with "buf".
Serial.println(buf);
Now my problem. If I replace the
byte newUid[] = {0x46,0x7E,0xE9,0xA9f};
with
byte newUid[] = {buf};
I get the error
invalid conversion from 'char*' to 'byte {aka unsigned char}'
How can I set my "newUid" as "buf"?
#define SS_PIN 0 //D2
#define RST_PIN 2 //D1
#include <SPI.h>
#include <MFRC522.h>
/* For RFID */
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
char buf[40]; // For string to byte array convertor
void convert(char *s)
{
int i, j, k;
buf[0] = 0x0;
for (j = 0, i = 0, k = 0; j < strlen(s); j++)
{
if (i++ == 0) {
buf[k++] = '0';
buf[k++] = 'x';
}
buf[k++] = s[j];
if (i == 2) {
if(j != strlen(s) -1) buf[k++] = ',';
i = 0;
}
}
buf[k] = 0x0;
}
void clone() {
/* RFID Read */
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent())
{
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial())
{
return;
}
//Show UID on serial monitor
Serial.println();
Serial.print(" UID tag :");
// Very basic UID dump
unsigned int hex_num;
hex_num = mfrc522.uid.uidByte[0] << 24;
hex_num += mfrc522.uid.uidByte[1] << 16;
hex_num += mfrc522.uid.uidByte[2] << 8;
hex_num += mfrc522.uid.uidByte[3];
// Get UID
int NFC_id = (int)hex_num;
Serial.print(NFC_id, HEX);
// Convert UID to string using an int and a base (hexadecimal)
String stringUID = String(NFC_id, HEX);
char UIDChar[10];
stringUID.toCharArray(UIDChar,10);
delay(1000);
Serial.println();
// Convert to uppercase
for (int i = 0; i < strlen(UIDChar); i++ )
{
if ( UIDChar[i] == NULL ) break;
UIDChar[i] = toupper(UIDChar[i]);
}
//Serial.print( &UIDChar[0] );
Serial.println();
convert(UIDChar);
Serial.println(buf);
/* RFID Write */
// Set new UID
// Change your UID hex string to 4 byte array
// I get error if I use byte newUid[] = {buf};
/* ERROR HERE */
byte newUid[] = {0x46,0x7E,0xE9,0xA9};
if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) {
Serial.println( "Wrote new UID to card." );
}
// Halt PICC and re-select it so DumpToSerial doesn't get confused
mfrc522.PICC_HaltA();
if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
return;
}
// Dump the new memory contents
Serial.println( "New UID and contents:" );
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
}
void setup() {
Serial.begin ( 115200 );
/* RFID */
SPI.begin(); // Initiate SPI bus
mfrc522.PCD_Init(); // Initiate MFRC522
clone();
}
void loop() {
}
When you write
byte newUid[] = {buf};
you are trying to initialise newUid with a single element (there's only one item inside your {}), and that element is buf, which is a char* (or a char[]). That's why you get the error - you are trying to assign an array with one char* to a variable whose elements are bytes.
Without reading your full code in detail, I don't know why you are trying to do this assignment, rather than just use your buf array as it is. But to fix the problem, you probably just want to use
byte* newUid = buf;

split url into different categories in c programming [duplicate]

I have a URL like this:
http://192.168.0.1:8080/servlet/rece
I want to parse the URL to get the values:
IP: 192.168.0.1
Port: 8080
page: /servlet/rece
How do I do that?
Personally, I steal the HTParse.c module from the W3C (it is used in the lynx Web browser, for instance). Then, you can do things like:
strncpy(hostname, HTParse(url, "", PARSE_HOST), size)
The important thing about using a well-established and debugged library is that you do not fall into the typical
traps of URL parsing (many regexps fail when the host is an IP address, for instance, specially an IPv6 one).
I wrote a simple code using sscanf, which can parse very basic URLs.
#include <stdio.h>
int main(void)
{
const char text[] = "http://192.168.0.2:8888/servlet/rece";
char ip[100];
int port = 80;
char page[100];
sscanf(text, "http://%99[^:]:%99d/%99[^\n]", ip, &port, page);
printf("ip = \"%s\"\n", ip);
printf("port = \"%d\"\n", port);
printf("page = \"%s\"\n", page);
return 0;
}
./urlparse
ip = "192.168.0.2"
port = "8888"
page = "servlet/rece"
May be late,...
what I have used, is - the http_parser_parse_url() function and the required macros separated out from Joyent/HTTP parser lib - that worked well, ~600LOC.
With a regular expression if you want the easy way. Otherwise use FLEX/BISON.
You could also use a URI parsing library
Libcurl now has curl_url_get() function that can extract host, path, etc.
Example code: https://curl.haxx.se/libcurl/c/parseurl.html
/* extract host name from the parsed URL */
uc = curl_url_get(h, CURLUPART_HOST, &host, 0);
if(!uc) {
printf("Host name: %s\n", host);
curl_free(host);
}
This one has reduced size and worked excellent for me http://draft.scyphus.co.jp/lang/c/url_parser.html . Just two files (*.c, *.h). I had to adapt code [1].
[1]Change all the function calls from http_parsed_url_free(purl) to parsed_url_free(purl)
//Rename the function called
//http_parsed_url_free(purl);
parsed_url_free(purl);
Pure sscanf() based solution:
//Code
#include <stdio.h>
int
main (int argc, char *argv[])
{
char *uri = "http://192.168.0.1:8080/servlet/rece";
char ip_addr[12], path[100];
int port;
int uri_scan_status = sscanf(uri, "%*[^:]%*[:/]%[^:]:%d%s", ip_addr, &port, path);
printf("[info] URI scan status : %d\n", uri_scan_status);
if( uri_scan_status == 3 )
{
printf("[info] IP Address : '%s'\n", ip_addr);
printf("[info] Port: '%d'\n", port);
printf("[info] Path : '%s'\n", path);
}
return 0;
}
However, keep in mind that this solution is tailor made for [protocol_name]://[ip_address]:[port][/path] type of URI's. For understanding more about the components present in the syntax of URI, you can head over to RFC 3986.
Now let's breakdown our tailor made format string : "%*[^:]%*[:/]%[^:]:%d%s"
%*[^:] helps to ignore the protocol/scheme (eg. http, https, ftp, etc.)
It basically captures the string from the beginning until it encounters the : character for the first time. And since we have used * right after the % character, therefore the captured string will be ignored.
%*[:/] helps to ignore the separator that sits between the protocol and the IP address, i.e. ://
%[^:] helps to capture the string present after the separator, until it encounters :. And this captured string is nothing but the IP address.
:%d helps to capture the no. sitting right after the : character (the one which was encountered during the capturing of IP address). The no. captured over here is basically your port no.
%s as you may know, will help you to capture the remaining string which is nothing but the path of the resource you are looking for.
This C gist could be useful. It implements a pure C solution with sscanf.
https://github.com/luismartingil/per.scripts/tree/master/c_parse_http_url
It uses
// Parsing the tmp_source char*
if (sscanf(tmp_source, "http://%99[^:]:%i/%199[^\n]", ip, &port, page) == 3) { succ_parsing = 1;}
else if (sscanf(tmp_source, "http://%99[^/]/%199[^\n]", ip, page) == 2) { succ_parsing = 1;}
else if (sscanf(tmp_source, "http://%99[^:]:%i[^\n]", ip, &port) == 2) { succ_parsing = 1;}
else if (sscanf(tmp_source, "http://%99[^\n]", ip) == 1) { succ_parsing = 1;}
(...)
I wrote this
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct
{
const char* protocol = 0;
const char* site = 0;
const char* port = 0;
const char* path = 0;
} URL_INFO;
URL_INFO* split_url(URL_INFO* info, const char* url)
{
if (!info || !url)
return NULL;
info->protocol = strtok(strcpy((char*)malloc(strlen(url)+1), url), "://");
info->site = strstr(url, "://");
if (info->site)
{
info->site += 3;
char* site_port_path = strcpy((char*)calloc(1, strlen(info->site) + 1), info->site);
info->site = strtok(site_port_path, ":");
info->site = strtok(site_port_path, "/");
}
else
{
char* site_port_path = strcpy((char*)calloc(1, strlen(url) + 1), url);
info->site = strtok(site_port_path, ":");
info->site = strtok(site_port_path, "/");
}
char* URL = strcpy((char*)malloc(strlen(url) + 1), url);
info->port = strstr(URL + 6, ":");
char* port_path = 0;
char* port_path_copy = 0;
if (info->port && isdigit(*(port_path = (char*)info->port + 1)))
{
port_path_copy = strcpy((char*)malloc(strlen(port_path) + 1), port_path);
char * r = strtok(port_path, "/");
if (r)
info->port = r;
else
info->port = port_path;
}
else
info->port = "80";
if (port_path_copy)
info->path = port_path_copy + strlen(info->port ? info->port : "");
else
{
char* path = strstr(URL + 8, "/");
info->path = path ? path : "/";
}
int r = strcmp(info->protocol, info->site) == 0;
if (r && info->port == "80")
info->protocol = "http";
else if (r)
info->protocol = "tcp";
return info;
}
Test
int main()
{
URL_INFO info;
split_url(&info, "ftp://192.168.0.1:8080/servlet/rece");
printf("Protocol: %s\nSite: %s\nPort: %s\nPath: %s\n", info.protocol, info.site, info.port, info.path);
return 0;
}
Out
Protocol: ftp
Site: 192.168.0.1
Port: 8080
Path: /servlet/rece
Write a custom parser or use one of the string replace functions to replace the separator ':' and then use sscanf().

Compare two ip subnet

How to decide if two ip are on the same subnet or not?
The only input is the ip address and the subnet mask!
What is the optimal way, using C/C++, to compute if two ip have the same subnet?
bool checkForSubnetEquality(in_addr_t ipA, in_addr_t ipB, uint32_t subnetMask) {
return (ipA & subnetMask) == (ipB & subnetMask);
}
typedef unsigned char BYTE;
Bool CheckForSubnetParity(
BYTE[] _In_ iPAddress1,
BYTE[] _In_ iPAddress2,
BYTE[] _In_ subNetMask
) {
BYTE[] NetworkPrefix1 = new BYTE[4];
BYTE[] NetWorkPrefix2 = new BYTE[4];
Bool Result = true;
for ( int x = 0; x < 4; x++)
{
NetworkPrefix1[x] = iPAddress1[x] && subNetMask[x];
NetworkPrefix2[x] = iPAddress2[x] && subNetMask[x];
if ( NetworkPrefix1[x] != NetworkPrefix2[x] )
{
Result = false;
}
}
return Result;
}
You can apply 'xor' operation to both IP's using their mask and compare them after that. If they're identical, then both IP addresses in the same subnet.
Let's look at the 172.16.2.4/255.255.0.0 and 172.16.1.69/255.255.0.0
After 'xor' you'll get '172.16.0.0' for both addresses, so they are in the same subnet.
Regards.

c calculate all possible subnets

I'm trying to calculate all possible subnets with my Network IP and the Mask:
char *net = "192.168.1.0"
char *mask = "255.255.255.0"
in_addr_t _net = inet_addr(net);
in_addr_t _mask = inet_addr(mask);
int possibleHosts = ntohl(~_mask); // = 255
for (int i=0; i<possibleHosts; i++) { //From 0 to 255
in_addr_t subnet = (_mask & _net) | i; // should calculate the subnets from 192.168.1.0 to 192.168.1.255
struct in_addr in;
in.s_addr = subnet;
char *test = inet_ntoa(in);
printf("%s\n", test); //always prints 192.168.1.0???
}
But the output is always "192.168.1.0". Does anyone know why?
Assuming you want to keep your existing code (and thats a big assumption), remember that _net and _mask are both still in network order, so your OR'd in sub address needs to be as well.
const char *net = "192.168.1.0";
const char *mask = "255.255.255.0";
in_addr_t _net = inet_addr(net);
in_addr_t _mask = inet_addr(mask);
int possibleHosts = ntohl(~_mask);
for (int i=0; i<possibleHosts; i++)
{
in_addr_t subnet = (_mask & _net) | htonl(i); // <<== must be network-order
struct in_addr in;
in.s_addr = subnet;
char *test = inet_ntoa(in);
printf("%s\n", test);
}
Output
192.168.1.0
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5
192.168.1.6
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.10
192.168.1.11
192.168.1.12
192.168.1.13
192.168.1.14
192.168.1.15
192.168.1.16
192.168.1.17
192.168.1.18
192.168.1.19
192.168.1.20
192.168.1.21
192.168.1.22
192.168.1.23
192.168.1.24
192.168.1.25
192.168.1.26
192.168.1.27
192.168.1.28
192.168.1.29
192.168.1.30
192.168.1.31
192.168.1.32
192.168.1.33
192.168.1.34
192.168.1.35
192.168.1.36
192.168.1.37
192.168.1.38
192.168.1.39
192.168.1.40
192.168.1.41
192.168.1.42
192.168.1.43
192.168.1.44
192.168.1.45
192.168.1.46
192.168.1.47
192.168.1.48
192.168.1.49
192.168.1.50
192.168.1.51
192.168.1.52
192.168.1.53
192.168.1.54
192.168.1.55
192.168.1.56
192.168.1.57
192.168.1.58
192.168.1.59
192.168.1.60
192.168.1.61
192.168.1.62
192.168.1.63
192.168.1.64
192.168.1.65
192.168.1.66
192.168.1.67
192.168.1.68
192.168.1.69
192.168.1.70
192.168.1.71
192.168.1.72
192.168.1.73
192.168.1.74
192.168.1.75
192.168.1.76
192.168.1.77
192.168.1.78
192.168.1.79
192.168.1.80
192.168.1.81
192.168.1.82
192.168.1.83
192.168.1.84
192.168.1.85
192.168.1.86
192.168.1.87
192.168.1.88
192.168.1.89
192.168.1.90
192.168.1.91
192.168.1.92
192.168.1.93
192.168.1.94
192.168.1.95
192.168.1.96
192.168.1.97
192.168.1.98
192.168.1.99
192.168.1.100
192.168.1.101
192.168.1.102
192.168.1.103
192.168.1.104
192.168.1.105
192.168.1.106
192.168.1.107
192.168.1.108
192.168.1.109
192.168.1.110
192.168.1.111
192.168.1.112
192.168.1.113
192.168.1.114
192.168.1.115
192.168.1.116
192.168.1.117
192.168.1.118
192.168.1.119
192.168.1.120
192.168.1.121
192.168.1.122
192.168.1.123
192.168.1.124
192.168.1.125
192.168.1.126
192.168.1.127
192.168.1.128
192.168.1.129
192.168.1.130
192.168.1.131
192.168.1.132
192.168.1.133
192.168.1.134
192.168.1.135
192.168.1.136
192.168.1.137
192.168.1.138
192.168.1.139
192.168.1.140
192.168.1.141
192.168.1.142
192.168.1.143
192.168.1.144
192.168.1.145
192.168.1.146
192.168.1.147
192.168.1.148
192.168.1.149
192.168.1.150
192.168.1.151
192.168.1.152
192.168.1.153
192.168.1.154
192.168.1.155
192.168.1.156
192.168.1.157
192.168.1.158
192.168.1.159
192.168.1.160
192.168.1.161
192.168.1.162
192.168.1.163
192.168.1.164
192.168.1.165
192.168.1.166
192.168.1.167
192.168.1.168
192.168.1.169
192.168.1.170
192.168.1.171
192.168.1.172
192.168.1.173
192.168.1.174
192.168.1.175
192.168.1.176
192.168.1.177
192.168.1.178
192.168.1.179
192.168.1.180
192.168.1.181
192.168.1.182
192.168.1.183
192.168.1.184
192.168.1.185
192.168.1.186
192.168.1.187
192.168.1.188
192.168.1.189
192.168.1.190
192.168.1.191
192.168.1.192
192.168.1.193
192.168.1.194
192.168.1.195
192.168.1.196
192.168.1.197
192.168.1.198
192.168.1.199
192.168.1.200
192.168.1.201
192.168.1.202
192.168.1.203
192.168.1.204
192.168.1.205
192.168.1.206
192.168.1.207
192.168.1.208
192.168.1.209
192.168.1.210
192.168.1.211
192.168.1.212
192.168.1.213
192.168.1.214
192.168.1.215
192.168.1.216
192.168.1.217
192.168.1.218
192.168.1.219
192.168.1.220
192.168.1.221
192.168.1.222
192.168.1.223
192.168.1.224
192.168.1.225
192.168.1.226
192.168.1.227
192.168.1.228
192.168.1.229
192.168.1.230
192.168.1.231
192.168.1.232
192.168.1.233
192.168.1.234
192.168.1.235
192.168.1.236
192.168.1.237
192.168.1.238
192.168.1.239
192.168.1.240
192.168.1.241
192.168.1.242
192.168.1.243
192.168.1.244
192.168.1.245
192.168.1.246
192.168.1.247
192.168.1.248
192.168.1.249
192.168.1.250
192.168.1.251
192.168.1.252
192.168.1.253
192.168.1.254
Core Concept
mask32 = 0xFFFFFFFF = (~0 << 0);
mask31 = 0xFFFFFFFE = (~0 << 1);
mask30 = 0xFFFFFFFB = (~0 << 2);
mask01 = 0x00000000 = (~0 << 31);
[Note: ~0 means inverting all bits of 0, i.e. 0xFFFFFFFF. "<<" means left bit shift.]
Formula
uint32_t mask[ 33 ];
for i = 1 to 32:
mask[i] = ~0 << (32 - i);
Additional
// A function to convert mask from number format to string format.
// See http://www.allredroster.com/iptodec.htm for reference
// Example: maskAsString( 0xC0A80100 ) --> "192.168.1.0"
char const * maskAsString( uint32_t mask );
// .. and also the inverse function
uint32_t stringAsMask( char const * maskString );

Best ways of parsing a URL using C?

I have a URL like this:
http://192.168.0.1:8080/servlet/rece
I want to parse the URL to get the values:
IP: 192.168.0.1
Port: 8080
page: /servlet/rece
How do I do that?
Personally, I steal the HTParse.c module from the W3C (it is used in the lynx Web browser, for instance). Then, you can do things like:
strncpy(hostname, HTParse(url, "", PARSE_HOST), size)
The important thing about using a well-established and debugged library is that you do not fall into the typical
traps of URL parsing (many regexps fail when the host is an IP address, for instance, specially an IPv6 one).
I wrote a simple code using sscanf, which can parse very basic URLs.
#include <stdio.h>
int main(void)
{
const char text[] = "http://192.168.0.2:8888/servlet/rece";
char ip[100];
int port = 80;
char page[100];
sscanf(text, "http://%99[^:]:%99d/%99[^\n]", ip, &port, page);
printf("ip = \"%s\"\n", ip);
printf("port = \"%d\"\n", port);
printf("page = \"%s\"\n", page);
return 0;
}
./urlparse
ip = "192.168.0.2"
port = "8888"
page = "servlet/rece"
May be late,...
what I have used, is - the http_parser_parse_url() function and the required macros separated out from Joyent/HTTP parser lib - that worked well, ~600LOC.
With a regular expression if you want the easy way. Otherwise use FLEX/BISON.
You could also use a URI parsing library
Libcurl now has curl_url_get() function that can extract host, path, etc.
Example code: https://curl.haxx.se/libcurl/c/parseurl.html
/* extract host name from the parsed URL */
uc = curl_url_get(h, CURLUPART_HOST, &host, 0);
if(!uc) {
printf("Host name: %s\n", host);
curl_free(host);
}
This one has reduced size and worked excellent for me http://draft.scyphus.co.jp/lang/c/url_parser.html . Just two files (*.c, *.h). I had to adapt code [1].
[1]Change all the function calls from http_parsed_url_free(purl) to parsed_url_free(purl)
//Rename the function called
//http_parsed_url_free(purl);
parsed_url_free(purl);
Pure sscanf() based solution:
//Code
#include <stdio.h>
int
main (int argc, char *argv[])
{
char *uri = "http://192.168.0.1:8080/servlet/rece";
char ip_addr[12], path[100];
int port;
int uri_scan_status = sscanf(uri, "%*[^:]%*[:/]%[^:]:%d%s", ip_addr, &port, path);
printf("[info] URI scan status : %d\n", uri_scan_status);
if( uri_scan_status == 3 )
{
printf("[info] IP Address : '%s'\n", ip_addr);
printf("[info] Port: '%d'\n", port);
printf("[info] Path : '%s'\n", path);
}
return 0;
}
However, keep in mind that this solution is tailor made for [protocol_name]://[ip_address]:[port][/path] type of URI's. For understanding more about the components present in the syntax of URI, you can head over to RFC 3986.
Now let's breakdown our tailor made format string : "%*[^:]%*[:/]%[^:]:%d%s"
%*[^:] helps to ignore the protocol/scheme (eg. http, https, ftp, etc.)
It basically captures the string from the beginning until it encounters the : character for the first time. And since we have used * right after the % character, therefore the captured string will be ignored.
%*[:/] helps to ignore the separator that sits between the protocol and the IP address, i.e. ://
%[^:] helps to capture the string present after the separator, until it encounters :. And this captured string is nothing but the IP address.
:%d helps to capture the no. sitting right after the : character (the one which was encountered during the capturing of IP address). The no. captured over here is basically your port no.
%s as you may know, will help you to capture the remaining string which is nothing but the path of the resource you are looking for.
This C gist could be useful. It implements a pure C solution with sscanf.
https://github.com/luismartingil/per.scripts/tree/master/c_parse_http_url
It uses
// Parsing the tmp_source char*
if (sscanf(tmp_source, "http://%99[^:]:%i/%199[^\n]", ip, &port, page) == 3) { succ_parsing = 1;}
else if (sscanf(tmp_source, "http://%99[^/]/%199[^\n]", ip, page) == 2) { succ_parsing = 1;}
else if (sscanf(tmp_source, "http://%99[^:]:%i[^\n]", ip, &port) == 2) { succ_parsing = 1;}
else if (sscanf(tmp_source, "http://%99[^\n]", ip) == 1) { succ_parsing = 1;}
(...)
I wrote this
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct
{
const char* protocol = 0;
const char* site = 0;
const char* port = 0;
const char* path = 0;
} URL_INFO;
URL_INFO* split_url(URL_INFO* info, const char* url)
{
if (!info || !url)
return NULL;
info->protocol = strtok(strcpy((char*)malloc(strlen(url)+1), url), "://");
info->site = strstr(url, "://");
if (info->site)
{
info->site += 3;
char* site_port_path = strcpy((char*)calloc(1, strlen(info->site) + 1), info->site);
info->site = strtok(site_port_path, ":");
info->site = strtok(site_port_path, "/");
}
else
{
char* site_port_path = strcpy((char*)calloc(1, strlen(url) + 1), url);
info->site = strtok(site_port_path, ":");
info->site = strtok(site_port_path, "/");
}
char* URL = strcpy((char*)malloc(strlen(url) + 1), url);
info->port = strstr(URL + 6, ":");
char* port_path = 0;
char* port_path_copy = 0;
if (info->port && isdigit(*(port_path = (char*)info->port + 1)))
{
port_path_copy = strcpy((char*)malloc(strlen(port_path) + 1), port_path);
char * r = strtok(port_path, "/");
if (r)
info->port = r;
else
info->port = port_path;
}
else
info->port = "80";
if (port_path_copy)
info->path = port_path_copy + strlen(info->port ? info->port : "");
else
{
char* path = strstr(URL + 8, "/");
info->path = path ? path : "/";
}
int r = strcmp(info->protocol, info->site) == 0;
if (r && info->port == "80")
info->protocol = "http";
else if (r)
info->protocol = "tcp";
return info;
}
Test
int main()
{
URL_INFO info;
split_url(&info, "ftp://192.168.0.1:8080/servlet/rece");
printf("Protocol: %s\nSite: %s\nPort: %s\nPath: %s\n", info.protocol, info.site, info.port, info.path);
return 0;
}
Out
Protocol: ftp
Site: 192.168.0.1
Port: 8080
Path: /servlet/rece
Write a custom parser or use one of the string replace functions to replace the separator ':' and then use sscanf().

Resources