split url into different categories in c programming [duplicate] - 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().

Related

Trouble with implementing a Direct Module Method in Azure IoT Edge Module using azure-iot-sdk-c SDK

Full disclosure, I have asked this question on Azure IoT SDK C github project, but since they recommend looking on StackOverflow, I decided to post here as well.
I am having trouble implementing a Direct Module Method handler in my azure-iot-sdk-c based IoT Edge Module. I could not find a documentation page with an example implementation, so I assembled my implementation from various SDK documentation pages and unit test "examples".
To test this, I have a dedicated Linux based PC (Ubuntu 18.04) running iotedge 1.0.8-2. I can see that my module is starting and printing its version and firing the connection status callback message. I even even see that the ModuleTwin callback is firing and printing the payload when I manually edit the module identity twin for my device in the portal.
However, when I try to manually invoke a Direct Method on my module within my device in the portal, I see nothing printed and I get the following error in the portal:
{"message":"GatewayTimeout:{\r\n \"Message\": \"{\\\"errorCode\\\":504101,\\\"trackingId\\\":\\\"8215e001484d41a19245639844f44f78-G:9-TimeStamp:01/14/2020 21:20:42-G:0-TimeStamp:01/14/2020 21:20:42\\\",\\\"message\\\":\\\"Timed out waiting for the response from device.\\\",\\\"info\\\":{},\\\"timestampUtc\\\":\\\"2020-01-14T21:20:42.0556758Z\\\"}\",\r\n \"ExceptionMessage\": \"\"\r\n}"}
The relevant code is below. I looked on StackOverflow but examples there are not C SDK based. Where am I going wrong with Direct Module Methods? Thank you!
Update: An interesting observation is that if I change this code to use MQTT from AMQP, then everything works. Is AMQP not supported for Direct Module Methods?
#include <iothub_module_client_ll.h>
#include <iothub_client_options.h>
#include <iothub_message.h>
#include <azure_c_shared_utility/threadapi.h>
#include <azure_c_shared_utility/crt_abstractions.h>
#include <azure_c_shared_utility/platform.h>
#include <azure_c_shared_utility/shared_util_options.h>
#include <iothubtransportamqp.h>
#include <iothub.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
// Linker defined build information (see Makefile)
extern char __BUILD_DATE;
extern char __BUILD_NUMBER;
// Set the default value for module communication (e.g. AMQP) log tracing, yet
// allow compile time overrides.
#ifndef LOG_TRACE_ENABLED
#define LOG_TRACE_ENABLED 0
#endif
static void moduleTwinCallback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* /*userContextCallback*/)
{
EPRINT("DEBUG: Module Twin callback called with (state=%s)", MU_ENUM_TO_STRING(DEVICE_TWIN_UPDATE_STATE, update_state));
EPRINT("DEBUG: payload=%.*s", (int)size, (const char *)payLoad);
fflush(NULL);
//JSON_Value *root_value = json_parse_string(payLoad);
//JSON_Object *root_object = json_value_get_object(root_value);
//if (json_object_dotget_value(root_object, "desired.TemperatureThreshold") != NULL) {
// temperatureThreshold = json_object_dotget_number(root_object, "desired.TemperatureThreshold");
//}
//if (json_object_get_value(root_object, "TemperatureThreshold") != NULL) {
// temperatureThreshold = json_object_get_number(root_object, "TemperatureThreshold");
//}
}
static int DirectMethodCb(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* /*userContextCallback*/)
{
const char *METHOD_NAME = "TestMethod";
const int METHOD_RESPONSE_SUCCESS = 200;
const int METHOD_RESPONSE_ERROR = 401;
int responseCode;
EPRINT("DEBUG: Method name: %s", method_name);
EPRINT("DEBUG: Method payload: %.*s", (int)size, (const char*)payload);
if (strcmp(METHOD_NAME, method_name))
{
EPRINT("Method name incorrect - expected %s but got %s", METHOD_NAME, method_name);
responseCode = METHOD_RESPONSE_ERROR;
}
/*
else if (size != strlen(expectedMethodPayload))
{
LogError("payload size incorect - expected %zu but got %zu", strlen(expectedMethodPayload), size);
responseCode = METHOD_RESPONSE_ERROR;
}
else if (memcmp(payload, expectedMethodPayload, size))
{
LogError("Payload strings do not match");
responseCode = METHOD_RESPONSE_ERROR;
}
*/
else
{
*resp_size = size;
if (size == 0)
{
*response = NULL;
EPRINT("DEBUG: Empty, but good response");
responseCode = METHOD_RESPONSE_SUCCESS;
}
else
{
if ((*response = (unsigned char*)malloc(*resp_size)) == NULL)
{
EPRINT("allocation failure");
responseCode = METHOD_RESPONSE_ERROR;
}
else
{
(void)memcpy(*response, payload, *resp_size);
EPRINT("DEBUG: All good - echoing back the payload");
responseCode = METHOD_RESPONSE_SUCCESS;
}
}
}
EPRINT("DEBUG: completing with return code %d", responseCode);
fflush(NULL);
return responseCode;
}
static void ConnectionStatusCb(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* /*userContextCallback*/)
{
EPRINT("DEBUG: ConnectionStatusCb(status=%d %s, reason=%d %s",
result, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS, result),
reason, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, reason)
);
fflush(NULL);
}
int main(void)
{
IOTHUB_MODULE_CLIENT_LL_HANDLE iotHubModuleClientHandle = nullptr;
int retval = 1;
do
{
printf("\n\n=======================\n");
printf("Build date : %lu\n", (unsigned long) &__BUILD_DATE);
printf("Build number: %lu\n", (unsigned long) &__BUILD_NUMBER);
fflush(NULL);
srand((unsigned int)time(NULL));
if (0 != IoTHub_Init())
{
EPRINT("Failed to initialize the platform.");
break;
}
iotHubModuleClientHandle = IoTHubModuleClient_LL_CreateFromEnvironment(AMQP_Protocol);
if (nullptr == iotHubModuleClientHandle)
{
EPRINT("IoTHubModuleClient_LL_CreateFromEnvironment failed");
break;
}
IOTHUB_CLIENT_RESULT result = IoTHubModuleClient_LL_SetModuleMethodCallback(iotHubModuleClientHandle, DirectMethodCb, iotHubModuleClientHandle);
if (IOTHUB_CLIENT_OK != result)
{
EPRINT("IoTHubModuleClient_SetModuleMethodCallback failed: %d", result);
break;
}
result = IoTHubModuleClient_LL_SetConnectionStatusCallback(iotHubModuleClientHandle, ConnectionStatusCb, iotHubModuleClientHandle);
if (IOTHUB_CLIENT_OK != result)
{
EPRINT("IoTHubDeviceClient_SetConnectionStatusCallback failed: %d", result);
break;
}
#if LOG_TRACE_ENABLED
bool traceOn = true;
IoTHubModuleClient_LL_SetOption(iotHubModuleClientHandle, OPTION_LOG_TRACE, &traceOn);
#endif // LOG_TRACE_ENABLED
result = IoTHubModuleClient_LL_SetModuleTwinCallback(iotHubModuleClientHandle, moduleTwinCallback, iotHubModuleClientHandle);
if (IOTHUB_CLIENT_OK != result)
{
EPRINT("IoTHubModuleClient_LL_SetModuleTwinCallback failed: %d", result);
break;
}
while (true)
{
IoTHubModuleClient_LL_DoWork(iotHubModuleClientHandle);
ThreadAPI_Sleep(100);
}
} while(false);
if (nullptr != iotHubModuleClientHandle)
{
IoTHubModuleClient_LL_Destroy(iotHubModuleClientHandle);
}
IoTHub_Deinit();
return retval;
}

C: How to access value returned by Net-SNMP GET

I apologize for the naive question, Iam new to Net-SNMP. I have tried using this simple SNMP demo app given in Net-SNMP website.
This code performs a SNMP-GET and manipulates the response to check if the value returned is a ASN_OCTET_STRING, and if yes, access the string using vars->val.string and assigned to a character pointer sp.
But Iam unable to figure out how to access this value if the type is anything other than ASN_OCTET_STRING. For example how do I take this value and, say, assign it to a variable if it is of type 'ASN_INTEGER' or 'ASN_OBJECT_ID'.
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <string.h>
#define DEMO_USE_SNMP_VERSION_3
#ifdef DEMO_USE_SNMP_VERSION_3
const char *our_v3_passphrase = "MD5Password";
#endif
int main(int argc, char ** argv)
{
netsnmp_session session, *ss;
netsnmp_pdu *pdu;
netsnmp_pdu *response;
oid anOID[MAX_OID_LEN];
size_t anOID_len;
netsnmp_variable_list *vars;
int status;
int count=1;
init_snmp("snmpdemoapp");
snmp_sess_init( &session );
session.peername = strdup("localhost:161");
#ifdef DEMO_USE_SNMP_VERSION_3
session.version=SNMP_VERSION_3;
session.securityName = strdup("user2");
session.securityNameLen = strlen(session.securityName);
session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
session.securityAuthProto = usmHMACMD5AuthProtocol;
session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid);
session.securityAuthKeyLen = USM_AUTH_KU_LEN;
if (generate_Ku(session.securityAuthProto,
session.securityAuthProtoLen,
(u_char *) our_v3_passphrase, strlen(our_v3_passphrase),
session.securityAuthKey,
&session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
snmp_perror(argv[0]);
snmp_log(LOG_ERR,
"Error generating Ku from authentication pass phrase. \n");
exit(1);
}
#else /* we'll use the insecure (but simplier) SNMPv1 */
session.version = SNMP_VERSION_1;
session.community = "demopublic";
session.community_len = strlen(session.community);
#endif /* SNMPv1 */
SOCK_STARTUP;
ss = snmp_open(&session);
if (!ss) {
snmp_sess_perror("ack", &session);
SOCK_CLEANUP;
exit(1);
}
pdu = snmp_pdu_create(SNMP_MSG_GET);
anOID_len = MAX_OID_LEN;
if (!snmp_parse_oid("ip.21.1.8.xx.xx.xx.xx", anOID, &anOID_len)) {
snmp_perror("ip.21.1.8.xx.xx.xx.xx");
SOCK_CLEANUP;
exit(1);
}
snmp_add_null_var(pdu, anOID, anOID_len);
status = snmp_synch_response(ss, pdu, &response);
if (status == STAT_SUCCESS && response->errstat == SNMP_ERR_NOERROR) {
for(vars = response->variables; vars; vars = vars->next_variable)
print_variable(vars->name, vars->name_length, vars);
/* manipuate the information ourselves */
for(vars = response->variables; vars; vars = vars->next_variable) {
if (vars->type == ASN_OCTET_STR) {
char *sp = (char *)malloc(1 + vars->val_len);
memcpy(sp, vars->val.string, vars->val_len);
sp[vars->val_len] = '\0';
printf("value #%d is a string: %s\n", count++, sp); //Here sp now has the string - But this doesnt work when the string is for eg."HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0"
free(sp);
}
else if(vars->type == ASN_INTEGER) {
printf("value is an Integer\n");
int ObjVal;
// How do I get the Integer value and assign it to 'ObjVal'
}
else if(vars->type == ASN_OBJECT_ID) {
printf("value is an OID\n");
// How do I get the OID and assign it to some variable
}
else if(vars->type == ASN_TIMETICKS) {
printf("value is in Timeticks\n");
// How do I get the Timeticks and assign it to some variable for further processing
}
}
} else {
if (status == STAT_SUCCESS)
fprintf(stderr, "Error in packet\nReason: %s\n",
snmp_errstring(response->errstat));
else if (status == STAT_TIMEOUT)
fprintf(stderr, "Timeout: No response from %s.\n",
session.peername);
else
snmp_sess_perror("snmpdemoapp", ss);
}
if (response)
snmp_free_pdu(response);
snmp_close(ss);
SOCK_CLEANUP;
return (0);
}
Tried vars->val.integer or vars->val.object_id, but that doesnot contain the value. What am I missing here?
My another question is, even when it is of type ASN_OCTET_STRING, when the GET reply is something like this,
HOST-RESOURCES-MIB::hrSWInstalledDate.1953 = STRING: 0-1-1,0:0:0.0
then vars->val.string doesnt have "0-1-1,0:0:0.0" as string.
Basically my question is How does the value get stored in the response structure from which I can retrieve the values?
Thanks in Advance!!
P.S: Makefile link from Net-SNMP website.
Edit1:
For Integers, i can read using *vars->val->string as pointed out by immibis. Any Ideas about how to access other datatypes?
As you can see in /usr/include/net-snmp/types.h file or similar on your system, net-snmp vars->val has the following union type:
typedef union {
long *integer;
u_char *string;
oid *objid;
u_char *bitstring;
struct counter64 *counter64;
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
float *floatVal;
double *doubleVal;
/*
* t_union *unionVal;
*/
#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
} netsnmp_vardata;
also *vars has val_len field, where the length of data stored.
So you can access integer as *vars->val.integer, string as pointer to u_char vars->val.string with vars->val_len chars, oid as pointer to oid vars->val.objid with vars->val_len/sizeof(oid) oid elements and so on.

How to retrieve issuer alternative name for ssl certificate by openssl

I can get the subject alternative name like
X509_NAME_get_text_by_NID(X509_get_subject_name(x), NID_subject_alt_name, hc->https_domain_name, 256)
With same method by changing 2. parameter to NID_issuer_alt_name I am expecting to get issuer name like;
X509_NAME_get_text_by_NID(X509_get_subject_name(x), NID_issuer_alt_name, hc->https_ca_name, 256);
But instead I am getting a empty string . How can I retrieve issuer alternative name correctly?
You could try the following solution, as recommended in https://github.com/iSECPartners/ssl-conservatory :
static HostnameValidationResult matches_subject_alternative_name (const char *hostname, const X509 *server_cert) {
HostnameValidationResult result = MatchNotFound;
int i;
int san_names_nb = -1;
STACK_OF(GENERAL_NAME) *san_names = NULL;
// Try to extract the names within the SAN extension from the certificate
san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL);
if (san_names == NULL) {
return NoSANPresent;
}
san_names_nb = sk_GENERAL_NAME_num(san_names);
// Check each name within the extension
for (i=0; i<san_names_nb; i++) {
const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
if (current_name->type == GEN_DNS) {
// Current name is a DNS name, let's check it
char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
// Make sure there isn't an embedded NUL character in the DNS name
if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
result = MalformedCertificate;
break;
}
else { // Compare expected hostname with the DNS name
if (strcasecmp(hostname, dns_name) == 0) {
result = MatchFound;
break;
}
}
}
}
sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
return result;
}
Hope it helps !
In your call to X509_NAME_get_text_by_NID with the NID_issuer_alt_name constant, I would have replaced X509_get_subject_name(x) by X509_get_issuer_name(x). I think this should do the trick you are after.

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().

Calculate broadcast address from ip and subnet mask

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 :)

Resources