C / Youtube API Request > VLC - c

I'm trying to improve my tiny C application which streams YouTube videos.
For some reason I'm not being able to send request and decode JSON.
Could someone point me in the right direction, this is the original.
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#define SA struct sockaddr
#define MAXLINE 4096
#define LISTENQ 1024
void process_http(int sockfd, char argv[]){
char sendline[MAXLINE], recvline[MAXLINE];
char storeline[MAXLINE];
char run_command[50];
char * linkpos;
ssize_t n;
int arraysize;
int i;
arraysize = strlen(&argv[0]);
if(&argv[0] == "\32") {
printf("Har space\n");
}
strcpy(sendline,"GET /?youtubereq=");
strcat(sendline,&argv[0]);
strcat(sendline," /HTTP/1.0\r\n");
strcat(sendline,"Connection: Keep-Alive\r\n");
strcat(sendline,"Pragma: no-cache\r\n");
strcat(sendline,"Host: 127.0.0.1\r\n");
strcat(sendline,"Accept: www/source\r\n");
strcat(sendline,"Accept: text/html\r\n\r\n");
write(sockfd, sendline, strlen(sendline));
while ( ( n = read(sockfd, recvline, MAXLINE)) != 0 )
{
recvline[n]='\0';
strcat(storeline, recvline);
}
linkpos = strstr(storeline, "http://www.youtube.com/");
strcpy(run_command, "cvlc ");
strcat(run_command, linkpos);
system("clear");
printf("Playing video...\n");
system(run_command);
}
void encode(unsigned char *s, char *enc, char *tb)
{
for (; *s; s++) {
if (tb[*s]) sprintf(enc, "%c", tb[*s]);
else sprintf(enc, "%%%02X", *s);
while (*++enc);
}
}
int main(int argc, char* argv[])
{
int sockfd;
struct sockaddr_in servaddr;
char rfc3986[256] = {0};
char html5[256] = {0};
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(80);
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
char enc[sizeof(argv[1]) * 3];
int i;
for (i = 0; i < 256; i++) {
rfc3986[i] = isalnum(i)||i == '~'||i == '-'||i == '.'||i == '_'
? i : 0;
html5[i] = isalnum(i)||i == '*'||i == '-'||i == '.'||i == '_'
? i : (i == ' ') ? '+' : 0;
}
printf("Loading video, please wait...\n");
encode(argv[1], enc, rfc3986);
process_http(sockfd, enc);
return 0;
}
I'm currently relying on PHP/Apache to be run on localhost to do the API-Request for me, this is far from optimal. but as stated above can't seem to be able to implement that part in this code.
(I'm quite new to C)
The PHP Code is as follows.
<?php
if($_GET['youtubereq']) {
$req = $_GET['youtubereq'];
$req = urlencode($req);
$build_yt_url = "http://gdata.youtube.com/feeds/api/videos?q='" . $req . "'&format=5&max-results=1&v=2&alt=jsonc";
$response = file_get_contents($build_yt_url);
$response = json_decode($response, true);
$raw_url = "http://www.youtube.com/watch?v=" . $response["data"]["items"][0]["id"];
echo $raw_url;
}
else {
echo ".";
}
?>
Program works like this ./youtubeplayer "some video to search for"
Ideas?

You're complicating. You can run php from the command line:
<?php
if ($argc != 2) die("wrong params\n");
$build_yt_url = "http://gdata.youtube.com/feeds/api/videos?q='"
. urlencode($argv[1]) . "'&format=5&max-results=1&v=2&alt=jsonc";
$response = file_get_contents($build_yt_url) or die("error fetching data\n");
$response = json_decode($response, true);
if (!isset($response["data"]["items"][0]["id"]))
die("error finding in result\n");
$raw_url = "http://www.youtube.com/watch?v=" .
$response["data"]["items"][0]["id"];
system("cvlc " . escapeshellarg($raw_url));
Then run with:
php myscript.php cat

Related

UDP server not responding to client

Working on a program that is meant to emulate data layers in networking. I've got messages coming through to the server correctly, however, the client is not receiving the ACK frame from the server. This is causing my program to wait endlessly. Any help in fixing the matter is appreciated.
Sender
#include <stdio.h>
#include <unistd.h>
#define MAXFRAME 97
main(int argc, char* argv[]){
char *frame;
int len = 0;
int c;
dlinits("spirit.cba.csuohio.edu", 43525);
frame = malloc(MAXFRAME);
FILE *file = fopen(argv[1], "r");
if (file == NULL)
return NULL;
while ((c = fgetc(file)) != EOF)
{
if(len == (MAXFRAME-1)){
dlsend(frame, len, 0);
len = 0;
memset(frame,0,strlen(frame));
}
frame[len++] = (char) c;
}
dlsend(frame, len, 1);
}
Receiver
#include <string.h>
#include <unistd.h>
char* dlrecv();
main(){
char* test[100];
dlinitr(43525);
while(1){
strcpy(test,dlrecv());
printf("%s\n", test);
}
}
Data Layer
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFMAX 100
static int sk;
static struct sockaddr_in remote;
static struct sockaddr_in local;
static int fnum = 0;
static expFra = 0x00;
dlinits(char* host, int port){//initialize sender
struct hostent *hp;
sk = socket(AF_INET, SOCK_DGRAM, 0);
remote.sin_family = AF_INET;
hp = gethostbyname(host);
if (hp == NULL){
printf("Can't find host name\n");
exit(1);
}
bcopy(hp->h_addr,&remote.sin_addr.s_addr,hp->h_length);
remote.sin_port = ntohs(port);
}
dlinitr(int port){//initialize receiver
int rlen = sizeof(remote);
int len = sizeof(local);
char buf[BUFMAX];
sk = socket(AF_INET,SOCK_DGRAM,0);
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(port);
bind (sk, &local,sizeof(local));
getsockname(sk,&local,&len);
}
dlsend(char* msg, int len, int end){//send data
int header = 0x00;
int result;
char *ackframe = malloc(3);
unsigned char *nmsg;
nmsg = malloc(100);
if ((fnum%2) == 1){
header = header|0x02;
}
if (end == 1){
header = header|0x40;
}
header = header^0xff;
printf("%x\n %x\n", header, 0);
nmsg[0] = (char)header;
len++;
printf("%s\n", nmsg);
memcpy(nmsg + 1, msg, strlen(msg));
result = crc(nmsg, len);
nmsg[len++] = ((result >> 8) & 0xff);
nmsg[len++] = (result & 0xff);
printf("%s\n", nmsg);
sendto(sk,nmsg,len,0,&remote,sizeof(remote));
read(sk,ackframe,3);
printf("Ack Received: %s\n", ackframe);
fnum++;
}
char* dlrecv(){//receive data
int result;
int header;
int ACK = 1;
char alen = 1;
char *ackframe = malloc(3);
unsigned char* msg = malloc(100);
while (ACK){
recvfrom(sk,msg,BUFMAX,0,&remote,sizeof(remote));
int len = strlen(msg);
result = crc(msg, len);
if (result == 0){
msg[--len] = 0;
msg[--len] = 0;
header = msg[0];
printf("Header %x expFra %x\n", header, expFra);
header = header^0xff;
printf("Header %x expFra %x\n", header, expFra);
if ((header<<4) == (expFra<<4)){
expFra = expFra^0x02;
ackframe[0] = (0x10|header);
result = crc(ackframe, alen);
ackframe[alen++] = ((result >> 8) & 0xff);
ackframe[alen++] = (result & 0xff);
sendto(sk,ackframe,strlen(ackframe),0,&remote,sizeof(remote));
printf("Ack Sent: %s\n", ackframe);
ACK = 0;
}
}
}
printf("%s\n", msg);
return ++msg;
}
EDIT for the moment these are working on the same machine.
EDIT I ran a check using errno, which returned error 22 for the sendto inside of dlrecv.
My experience with UDP has been that read() (which you're using at the end of your dlsend()) is very hit-or-miss, especially when paired with sendto(). Unless there's a good reason not to do it, changing read() to recvfrom() should fix the problem.
Your code also throws a lot of warnings for mismatched types. They're kinda-sorta harmless, but make tracking anything else down more complicated.
After that, the final acknowledgment-sendto() is using bad socket data. Poking around, the reason is that you're passing an integer in (sizeof(remote)) as a pointer to the address's size in the previous recvfrom() call. If the initial size given is too small, recvfrom() produces unreliable results. If it needs less space than that, it'll change that value to tell you what it used.
So, you need to declare a integer initialized to the size of a sockaddr_in structure, and pass a pointer to it as that last parameter. With those changes, assuming the server arrives at the sendto() function (your sample only has it under a single conditional branch), you'll get the right values for the address and will be able to send the acknowledgment.
The big lessons learned should be (a) make sure all the types are correct and review every warning and (b) check the return value of every socket call and print the error if you get a -1 back.

C pointer segmentation fault

I have this C code:
#include <errno.h>
#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
char *buf;
int c,s; int port=45678;
int recv_m(int c,char *buf);
void get(){
char fileNameBuf[20];
int i=0;
char *s = buf;
if (*s=='/') {
s++;
while (*s!=' ') {
fileNameBuf[i]=*s;
*s++;
i++;
}
fileNameBuf[i]='\0';
}
}
int main(){
//server connected
while ((c = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0){
// Do whatever a web server does.
char recv_buf[50];
char *r=recv_buf;
while(recv(c, r , 1, 0)!=0){
//stores the received message in recv_buf
}
recv_buf[i]='\0';
if (strncmp(recv_buf, "GET ", 4)==0){
buf=recv_buf+4;
get();
}
}
return (0);
}
*buf points to string /index.html HTTP/1.0. At the end of the function, fileNameBuf should store the string index.html.
The number of times in the while loop should be 10. When I run this code, i = 381 and I get a segmentation fault (core dump).
What am I doing wrong?
Here is the whole code, so *buf is the problem?
Either your assumptions about what is in buf must be faulty — or we're faulty in our interpretation of what you mean when you say:
*buf points to string "/index.html HTTP/1.1".
If you declared char **buf; and set:
char *str = "/index.html HTTP/1.1";
char **buf = str;
Then *buf points to the start of the string. This is why creating an SSCCE (Short, Self-Contained, Correct Example) is important; it removes the ambiguities.
An SSCCE
This code:
#include <stdio.h>
const char *buf = "/index.html HTTP/1.1";
static
void get(void)
{
char fileNameBuf[10];
int i=0;
if (*buf=='/')
{
buf++;
while (*buf!=' ')
{
fileNameBuf[i]=*buf;
buf++;
i++;
printf("%d\n", i);
}
}
printf("%.*s\n", (int)sizeof(fileNameBuf), fileNameBuf);
}
int main(void)
{
get();
return 0;
}
produces this output:
1
2
3
4
5
6
7
8
9
10
index.html
Granted, I had to take care not to print beyond the end of the array. Your array is minimally sized; it cannot hold a string containing the file name (no space for the null terminator). But it should not crash — if char *buf = "/index.html HTTP/1.1";!
Completed code — stage 1
This is closely based on what was submitted as the program. It compiles cleanly — I've not tried running it.
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
char *buf;
int c, s; int port = 45678;
struct sockaddr_in server, client;
char *ipaddress = "127.0.0.1";
int clientlen = sizeof(client);
int portset = 0;
int recv_m(int c, char *buf);
static
void get(void)
{
printf("in get method\n");
char fileNameBuf[20];
int i = 0;
printf("%s\n", buf);
char *s = buf;
if (*s == '/')
{
printf("buf==/\n");
s++;
while (*s != ' ')
{
// printf("%c\n",*buf);
// printf("in while\n");
fileNameBuf[i] = *s;
s++;
i++;
printf("%d\n", i);
}
fileNameBuf[i]='\0';
printf("<<%s>>\n", fileNameBuf);
}
else
{
printf("!= '/'\n");
}
}
int main(void)
{
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
// if (!inet_aton(ipaddress, &server.sin_addr))
// fprintf (stderr, "inet_addr() conversion error\n");
s = socket(AF_INET, SOCK_STREAM, 0); // Create socket
if (!s) {
perror("socket");
exit(0);
}
if (bind(s, (struct sockaddr *) &server, sizeof(server)) < 0) {
perror("bind");
exit(0);
}
printf("binded\n");
if (listen(s, SOMAXCONN) < 0) {
perror("listen");
exit(0);
}
printf("Waiting for connection\n");
while ((c = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0)
{
// Do whatever a web server does.
printf("got connected\n");
char recv_buf[50];
char el[4] = "\r\n\r\n";
int h = 0; int i = 0;
char *r = recv_buf;
while (recv(c, r, 1, 0) != 0)
{
if (h == 4) break;
if (*r == el[h]) {
h++;
}
r++;
i++;
if (h == 4) break;
}
recv_buf[i] = '\0';
printf("%s\n", recv_buf);
if ( strncmp(recv_buf, "GET ", 4) == 0) {
printf("check get\n");
buf = recv_buf+4;
printf("%s\n", buf);
get();
}
}
return(0);
}
This is not an SSCCE. All the code related to setting up the socket and reading from the socket should be tangential to the problem at hand.
Reduced Code — Stage 2
The reduction process involves eliminating the inessential.
#include <stdio.h>
#include <string.h>
char *buf;
static void get(void)
{
printf("in get method\n");
char fileNameBuf[20];
int i = 0;
printf("%s\n", buf);
char *s = buf;
if (*s == '/')
{
printf("buf==/\n");
s++;
while (*s != ' ')
{
fileNameBuf[i] = *s;
s++;
i++;
printf("%d\n", i);
}
fileNameBuf[i]='\0';
printf("<<%s>>\n", fileNameBuf);
}
else
{
printf("!= '/'\n");
}
}
int main(void)
{
char recv_buf[50];
strcpy(recv_buf, "GET /index.html HTTP/1.1\r\n\r\n");
printf("<<%s>>\n", recv_buf);
if (strncmp(recv_buf, "GET ", 4) == 0)
{
printf("check get\n");
buf = recv_buf+4;
printf("%s\n", buf);
get();
}
return(0);
}
This too compiles cleanly; unfortunately, it also runs successfully for me (GCC 4.8.1, Mac OS X 10.8.4):
<<GET /index.html HTTP/1.1
>>
check get
/index.html HTTP/1.1
in get method
/index.html HTTP/1.1
buf==/
1
2
3
4
5
6
7
8
9
10
<<index.html>>
This happens sometimes; you are too ruthless in your clean-up. So, you have to go back to the previous code and remove things more slowly.
Retrenching — Stage 3
Let's take the full code from Stage 1 and run it locally. The browser can connect to localhost:45678/index.html and the output is:
binded
Waiting for connection
got connected
GET /index.html HTTP/1.1
Host: localhost:45678
check get
/index.html HTTP/1.1
Host: localhost:45678
in get method
/index.html HTTP/1.1
Host: localhost:45678
buf==/
1
2
3
4
5
6
7
8
9
10
<<index.html>>
There is nothing sent back to the waiting browser (it's still waiting, but will time out shortly). The code loops back to the next accept; it isn't clear that it shuts up shop properly, but it didn't crash on the first cycle.
So, this has been a somewhat fruitless exercise...your code seems to work OK. It should still be improved — starting off by making every one of those global variables into a local in main(), and then passing buf to a modified get() with the signature void get(char *buf).
Does the code you showed really crash for you? If so, what does the debugger say about why it crashes?
Bullet-proofing — Stage 4
After establishing that the string pointed to by buf was actually "/index.html\r\n\r\n" and not"/index.html HTTP/1.1\r\n\r\n"`, it is clear that I was remiss in not ensuring that the code would not read past the end of null terminated strings nor write past the ends of buffers. However, this is precisely why an SSCCE is so important, and why diagnostic printing is so important. If the question had included the actual string that was being scanned, it would have been a lot simpler to spot the issue.
This code is more nearly bullet-proof. Amongst other major changes, it attempts to read the request in a single recv() operation, rather than reading the request byte by byte. This puts the onus on avoiding an overflow on recv(). All the global variables are gone; buf is passed to get() as an argument. get() has been written to detect EOS and overlong names, as well as handling names up to the first space. It still has the debug code for each character in the file name. The code in main() has been dolled up to send back a response that is valid HTTP — or valid enough HTTP — with a bit of HTML that changes each time it is handled. It's interesting seeing the requests the browser makes. There's also an error reporting function that writes to standard error, takes a format string and arguments as with printf() et al, and also adds the correct error number and message for the system error, and then exits with a failure status. This makes error reporting less painful; a one-line call suffices for each error, instead of 3 or 4 lines (depending on your choice of formatting). The errors can be more expressive than perror() too.
#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <unistd.h>
static void err_exit(const char *fmt, ...);
static
void get(char *buf)
{
printf("in get method\n");
char fileNameBuf[256];
size_t i = 0;
printf("%s\n", buf);
char *s = buf;
if (*s == '/')
{
printf("buf==/\n");
s++;
while (*s != '\0' && *s != ' ' && i < sizeof(fileNameBuf))
{
printf("i = %3d: c = %3d = 0x%.2X = '%c'\n",
(int)i, *s, *s & 0xFF, isprint(*s) ? *s : '.');
fileNameBuf[i++] = *s++;
}
fileNameBuf[i]='\0';
printf("<<%s>>\n", fileNameBuf);
}
else
{
printf("!= '/'\n");
}
}
int main(void)
{
char *buf;
int fd;
int s;
int port = 45678;
struct sockaddr_in server, client;
int clientlen = sizeof(client);
int msgnum = 314159;
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
s = socket(AF_INET, SOCK_STREAM, 0);
if (!s)
err_exit("socket()\n");
if (bind(s, (struct sockaddr *) &server, sizeof(server)) < 0)
err_exit("bind()\n");
printf("bound to address\n");
if (listen(s, SOMAXCONN) < 0)
err_exit("listen()\n");
printf("Waiting for connection\n");
while ((fd = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0)
{
printf("got connection\n");
char recv_buf[4096];
char el[5] = "\r\n\r\n";
ssize_t length;
/* Read message in one go; leave space for a null at the end */
if ((length = recv(fd, recv_buf, sizeof(recv_buf)-1, 0)) > 0)
{
recv_buf[length] = '\0';
if (strstr(recv_buf, el) == 0)
err_exit("Incomplete message (%d bytes and no CRLF, CRLF pair)\n", length);
printf("%d: <<%s>>\n", (int)length, recv_buf);
if (strncmp(recv_buf, "GET ", 4) == 0)
{
printf("check get\n");
buf = recv_buf + 4;
printf("<<%s>>\n", buf);
get(buf);
char message[256];
char format1[] =
"<html><head><title>Hello World!</title></head>"
"<body><h1>This is no fun at all (%d).</h1></body></html>\r\n\r\n";
int msg_len = snprintf(message, sizeof(message), format1, msgnum++);
char format2[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %d\r\n"
"Content-Encoding: UTF-8\r\n\r\n%s";
char response[1024];
size_t nbytes = snprintf(response, sizeof(response), format2,
msg_len, message);
write(fd, response, nbytes);
}
}
close(fd);
}
return(0);
}
static void err_exit(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
It appears that buf is a char array. If so, you should access the buf using a char pointer. Try this:
int main () {
char buf[] = "/index.html HTTP/1.0";
char fileNameBuf[10];
int i=0;
char *s = buf;
if (*s=='/') {
s++;
while (*s!=' ') {
fileNameBuf[i]=*s;
*s++;
i++;
printf("%d\n",i);
}
}
}
If buf is an array of char and even though arrays and pointers have several things in common, doing buf++ is not legal by C.Here is a text from Kernighan/Ritchie C book. You have probably buf declared as an array as well.
There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a variable; constructions like a=pa and a++ are illegal.
Due to this reason, doing "arr++" would be an error in the following code.
int main() {
int arr[10];
int *ptr = arr;
arr++; // Would be illegal.
ptr++; // This is okay.
}

libpcap get MAC from AF_LINK sockaddr_dl (OSX)

I am trying to obtain the MAC addresses of all of my interface on OSX using C. The common ways to obtain it Linux dont work on BSD - from everything I have seen, you must obtain the interfaces and look for the ones that are of type AF_LINK. My problem is that the LLADDR(sockaddr_dl) gives me a whole bunch of data (which includes my MAC) and I dont know what format the data is in. For example; the following code will output:
Device: en1
link sdl_alen: 101 mac:
31:f8:1e:df:d6:22:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:b0:06:10:00:01:00:00:00:c0:02:10:00:01:00:00:00:00:00:00:00:00:00:00:00:40
:03:10:00:01:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:03:00:6c:6f:30:00:00:00:00:00:00:00:00:00:00:00:00:00:70:03:10:00:01:00:00:00:e0:
02:10:00:01:00:00:
My MAC is bolded. It seems that this is the format all of the time, but I would be a lot more comfortable if I could cast LLADDR(sockaddr_dl) to something. In the net/if_dl.h, LLADDR is defied as:
#define LLADDR(s) ((caddr_t)((s)->sdl_data + (s)->sdl_nlen))
which, as far as I can tell, is saying that the results are of type (void *) - no help.
Other posts like:
Having a problem figuring out how to get Ethernet interface info on Mac OS X using ioctl/SIOCGIFADDR/SIOCGIFCONF?
seem to think they have it figured out, but if you look through the code, you can see it will not work due to sdl_alen not being 6.
int main() {
pcap_if_t *alldevs;
pcap_if_t *d;
pcap_addr_t *alladdrs;
pcap_addr_t *a;
struct sockaddr_dl* link;
char eb[PCAP_ERRBUF_SIZE];
char *addr_buf[40];
if (pcap_findalldevs(&alldevs, eb) == -1) {
printf("no devs found\n");
return(-1);
}
for (d = alldevs; d != NULL; d = d->next) {
printf("Device: %s\n", d->name);
alladdrs = d->addresses;
for (a = alladdrs; a != NULL; a = a->next) {
if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
// MAC ADDRESS
//struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
link = (struct sockaddr_dl*)a->addr->sa_data;
char mac[link->sdl_alen];
caddr_t macaddr = LLADDR(link);
memcpy(mac, LLADDR(link), link->sdl_alen);
printf("link sdl_alen: %i\n", link->sdl_alen);
int i;
printf("mac: ");
for(i = 0; i<link->sdl_alen; i++){
printf("%02x:", (unsigned char)mac[i]);
}
printf("\n");
}
}
}
}
The problem is that you are casting the sockaddr->sa_data to sockaddr_dl instead of casting the sockaddr itself to sockaddr_dl. Keep in mind that sockaddr_dl is an OS X/BSD thing, so #ifdef that part for portability.
Don't do:
link = (struct sockaddr_dl*)a->addr->sa_data;
Do:
link = (struct sockaddr_dl*)a->addr;
Then you will get the correct sdl_alen and things will work with out any hacks. And if you want to really easily get the name of addresses that may be either AF_INET, AF_INET6, or AF_LINK use getnameinfo():
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if_dl.h>
int get_sock_len(struct sockaddr *sa)
{
switch (sa->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
case AF_LINK:
return sizeof(struct sockaddr_dl);
default:
return -1;
}
}
int get_numeric_address(struct sockaddr *sa, char *outbuf, size_t buflen) {
socklen_t len;
if ((len = get_sock_len(sa)) < 0) {
return -1;
}
if (getnameinfo(sa, len, outbuf, buflen, NULL, 0, NI_NUMERICHOST)) {
return -1;
}
return 0;
}
...
char buf[NI_MAXHOST];
if (!get_numeric_address(sa, buf, sizeof(buf))) { /* For some struct sockaddr *sa */
printf("address: %s\n", buf);
} else {
printf("doh!\n");
}
Here is what I ended up doing:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <errno.h>
#include <net/if_dl.h>
#include <pcap.h>
#include "mac.h"
int main() {
printf("en1: %s\n", lookupDeviceMac("vnic0"));
}
unsigned char *lookupDeviceMac(char *dev){
pcap_if_t *alldevs;
pcap_if_t *d;
pcap_addr_t *alladdrs;
pcap_addr_t *a;
struct sockaddr_dl* link;
char eb[PCAP_ERRBUF_SIZE];
char *ret = malloc(6);
if (pcap_findalldevs(&alldevs, eb) == -1) {
printf("%s\n", eb);
return(ret);
}
for (d = alldevs; d != NULL; d = d->next) {
if(strcmp(d->name, dev) == 0){
printf("Device: %s\n", d->name);
alladdrs = d->addresses;
for (a = alladdrs; a != NULL; a = a->next) {
if(a->addr->sa_family == AF_LINK && a->addr->sa_data != NULL){
// MAC ADDRESS
//struct sockaddr_dl *sdl = (struct sockaddr_dl *) a->addr->sa_data;
link = (struct sockaddr_dl*)a->addr->sa_data;
char mac[link->sdl_alen];
caddr_t macaddr = LLADDR(link);
memcpy(mac, LLADDR(link), link->sdl_alen);
if(link->sdl_alen == 6){
// Seen in some sample code
sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[0],
(unsigned char)mac[1],
(unsigned char)mac[2],
(unsigned char)mac[3],
(unsigned char)mac[4],
(unsigned char)mac[5]);
} else if(link->sdl_alen > 6) {
// This is what happens in OSX 10.6.5
sprintf(ret, "%02x:%02x:%02x:%02x:%02x:%02x",(unsigned char)mac[1],
(unsigned char)mac[2],
(unsigned char)mac[3],
(unsigned char)mac[4],
(unsigned char)mac[5],
(unsigned char)mac[6]);
}
return(ret);
}
}
}
}
}
I was trying to see all devices reported by pcap_findalldevs and ended up here looking for information on interpreting AF_LINK addresses on MAC OS.
I'm used to seeing the struct sockaddr standing in for a interface family and being immediately cast to the appropriate type and not writing code to access *sa_data*.
For what I wanted it was sufficient to use link_ntoa to convert the address to a human readable form.
#import <Foundation/Foundation.h>
#include <pcap.h>
#include <netinet/in.h>
#include <net/if_dl.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
pcap_if_t* allDevs = NULL;
char errbuff[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs(&allDevs, errbuff) <0) {
NSLog(#"Failed with error '%s'", errbuff);
}
else {
for (pcap_if_t* device = allDevs; device != NULL; device = device->next) {
for (pcap_addr_t* address = device->addresses; address != NULL; address = address->next) {
struct sockaddr* sa_addr = address->addr;
if (sa_addr->sa_family == AF_LINK) {
struct sockaddr_dl* link_addr = (struct sockaddr_dl*) sa_addr;
char* linkAddress = link_ntoa(link_addr);
NSLog(#"ntoa %s", linkAddress);
}
}
}
}
pcap_freealldevs(allDevs);
[pool drain];
return 0;
}
Running on my machine I get the following devices with AF_LINK entries.
2011-08-14 02:22:43.024 HomePlugToolHelper[12473:903] ntoa en0:0.16.cb.xx.x.xx
2011-08-14 02:22:43.027 HomePlugToolHelper[12473:903] ntoa fw0:0.16.cb.xx.xx.xx.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa en1:0.16.cb.x.xx.xx
2011-08-14 02:22:43.028 HomePlugToolHelper[12473:903] ntoa lo0

How to get MAC address of your machine using a C program?

I am working on Ubuntu. How can I get MAC address of my machine or an interface say eth0 using C program.
Much nicer than all this socket or shell madness is simply using sysfs for this:
the file /sys/class/net/eth0/address carries your mac adress as simple string you can read with fopen()/fscanf()/fclose(). Nothing easier than that.
And if you want to support other network interfaces than eth0 (and you probably want), then simply use opendir()/readdir()/closedir() on /sys/class/net/.
You need to iterate over all the available interfaces on your machine, and use ioctl with SIOCGIFHWADDR flag to get the mac address. The mac address will be obtained as a 6-octet binary array. You also want to skip the loopback interface.
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) { /* handle error*/ };
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ }
struct ifreq* it = ifc.ifc_req;
const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
success = 1;
break;
}
}
}
else { /* handle error */ }
}
unsigned char mac_address[6];
if (success) memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
}
You want to take a look at the getifaddrs(3) manual page. There is an example in C in the manpage itself that you can use. You want to get the address with the type AF_LINK.
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
int main()
{
struct ifreq s;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strcpy(s.ifr_name, "eth0");
if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) {
int i;
for (i = 0; i < 6; ++i)
printf(" %02x", (unsigned char) s.ifr_addr.sa_data[i]);
puts("\n");
return 0;
}
return 1;
}
Using getifaddrs you can get MAC address from the family AF_PACKET.
In order to display the MAC address to each interface, you can proceed like this:
#include <stdio.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>
int main (int argc, const char * argv[])
{
struct ifaddrs *ifaddr=NULL;
struct ifaddrs *ifa = NULL;
int i = 0;
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
}
else
{
for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if ( (ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET) )
{
struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr;
printf("%-8s ", ifa->ifa_name);
for (i=0; i <s->sll_halen; i++)
{
printf("%02x%c", (s->sll_addr[i]), (i+1!=s->sll_halen)?':':'\n');
}
}
}
freeifaddrs(ifaddr);
}
return 0;
}
Ideone
I have just write one and test it on gentoo in virtualbox.
// get_mac.c
#include <stdio.h> //printf
#include <string.h> //strncpy
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h> //ifreq
#include <unistd.h> //close
int main()
{
int fd;
struct ifreq ifr;
char *iface = "enp0s3";
unsigned char *mac = NULL;
memset(&ifr, 0, sizeof(ifr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name , iface , IFNAMSIZ-1);
if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) {
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
//display mac address
printf("Mac : %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
close(fd);
return 0;
}
Assuming that c++ code (c++11) is okay as well and the interface is known.
#include <cstdint>
#include <fstream>
#include <streambuf>
#include <regex>
using namespace std;
uint64_t getIFMAC(const string &ifname) {
ifstream iface("/sys/class/net/" + ifname + "/address");
string str((istreambuf_iterator<char>(iface)), istreambuf_iterator<char>());
if (str.length() > 0) {
string hex = regex_replace(str, std::regex(":"), "");
return stoull(hex, 0, 16);
} else {
return 0;
}
}
int main()
{
string iface = "eth0";
printf("%s: mac=%016llX\n", iface.c_str(), getIFMAC(iface));
}
On Linux, use the service of "Network Manager" over the DBus.
There is also good'ol shell program which can be invoke and the result grabbed (use an exec function under C):
$ /sbin/ifconfig | grep HWaddr
A very portable way is to parse the output of this command.
ifconfig | awk '$0 ~ /HWaddr/ { print $5 }'
Provided ifconfig can be run as the current user (usually can) and awk is installed (it often is). This will give you the mac address of the machine.
Expanding on the answer given by #user175104 ...
std::vector<std::string> GetAllFiles(const std::string& folder, bool recursive = false)
{
// uses opendir, readdir, and struct dirent.
// left as an exercise to the reader, as it isn't the point of this OP and answer.
}
bool ReadFileContents(const std::string& folder, const std::string& fname, std::string& contents)
{
// uses ifstream to read entire contents
// left as an exercise to the reader, as it isn't the point of this OP and answer.
}
std::vector<std::string> GetAllMacAddresses()
{
std::vector<std::string> macs;
std::string address;
// from: https://stackoverflow.com/questions/9034575/c-c-linux-mac-address-of-all-interfaces
// ... just read /sys/class/net/eth0/address
// NOTE: there may be more than one: /sys/class/net/*/address
// (1) so walk /sys/class/net/* to find the names to read the address of.
std::vector<std::string> nets = GetAllFiles("/sys/class/net/", false);
for (auto it = nets.begin(); it != nets.end(); ++it)
{
// we don't care about the local loopback interface
if (0 == strcmp((*it).substr(-3).c_str(), "/lo"))
continue;
address.clear();
if (ReadFileContents(*it, "address", address))
{
if (!address.empty())
{
macs.push_back(address);
}
}
}
return macs;
}
netlink socket is possible
man netlink(7) netlink(3) rtnetlink(7) rtnetlink(3)
#include <assert.h>
#include <stdio.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <unistd.h>
#define SZ 8192
int main(){
// Send
typedef struct {
struct nlmsghdr nh;
struct ifinfomsg ifi;
} Req_getlink;
assert(NLMSG_LENGTH(sizeof(struct ifinfomsg))==sizeof(Req_getlink));
int fd=-1;
fd=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
assert(0==bind(fd,(struct sockaddr*)(&(struct sockaddr_nl){
.nl_family=AF_NETLINK,
.nl_pad=0,
.nl_pid=getpid(),
.nl_groups=0
}),sizeof(struct sockaddr_nl)));
assert(sizeof(Req_getlink)==send(fd,&(Req_getlink){
.nh={
.nlmsg_len=NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.nlmsg_type=RTM_GETLINK,
.nlmsg_flags=NLM_F_REQUEST|NLM_F_ROOT,
.nlmsg_seq=0,
.nlmsg_pid=0
},
.ifi={
.ifi_family=AF_UNSPEC,
// .ifi_family=AF_INET,
.ifi_type=0,
.ifi_index=0,
.ifi_flags=0,
.ifi_change=0,
}
},sizeof(Req_getlink),0));
// Receive
char recvbuf[SZ]={};
int len=0;
for(char *p=recvbuf;;){
const int seglen=recv(fd,p,sizeof(recvbuf)-len,0);
assert(seglen>=1);
len += seglen;
if(((struct nlmsghdr*)p)->nlmsg_type==NLMSG_DONE||((struct nlmsghdr*)p)->nlmsg_type==NLMSG_ERROR)
break;
p += seglen;
}
struct nlmsghdr *nh=(struct nlmsghdr*)recvbuf;
for(;NLMSG_OK(nh,len);nh=NLMSG_NEXT(nh,len)){
if(nh->nlmsg_type==NLMSG_DONE)
break;
struct ifinfomsg *ifm=(struct ifinfomsg*)NLMSG_DATA(nh);
printf("#%d ",ifm->ifi_index);
#ifdef _NET_IF_H
#pragma GCC error "include <linux/if.h> instead of <net/if.h>"
#endif
// Part 3 rtattr
struct rtattr *rta=IFLA_RTA(ifm); // /usr/include/linux/if_link.h
int rtl=RTM_PAYLOAD(nh);
for(;RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl))switch(rta->rta_type){
case IFLA_IFNAME:printf("%s ",(const char*)RTA_DATA(rta));break;
case IFLA_ADDRESS:
printf("hwaddr ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
case IFLA_BROADCAST:
printf("bcast ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
case IFLA_PERM_ADDRESS:
printf("perm ");
for(int i=0;i<5;++i)
printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
break;
}
printf("\n");
}
close(fd);
fd=-1;
return 0;
}
Example
#1 lo hwaddr 00:00:00:00:00:00 bcast 00:00:00:00:00:00
#2 eth0 hwaddr 57:da:52:45:5b:1a bcast ff:ff:ff:ff:ff:ff perm 57:da:52:45:5b:1a
#3 wlan0 hwaddr 3c:7f:46:47:58:c2 bcast ff:ff:ff:ff:ff:ff perm 3c:7f:46:47:58:c2
This is a Bash line that prints all available mac addresses, except the loopback:
for x in `ls /sys/class/net |grep -v lo`; do cat /sys/class/net/$x/address; done
Can be executed from a C program.

How do I output my host’s IP addresses from a C program?

I need to display all the IP addresses from my local computer, using the C language. How can this be done?
#include <stdio.h>
#include <stropts.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/netdevice.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int print_addresses(const int domain)
{
int s;
struct ifconf ifconf;
struct ifreq ifr[50];
int ifs;
int i;
s = socket(domain, SOCK_STREAM, 0);
if (s < 0) {
perror("socket");
return 0;
}
ifconf.ifc_buf = (char *) ifr;
ifconf.ifc_len = sizeof ifr;
if (ioctl(s, SIOCGIFCONF, &ifconf) == -1) {
perror("ioctl");
return 0;
}
ifs = ifconf.ifc_len / sizeof(ifr[0]);
printf("interfaces = %d:\n", ifs);
for (i = 0; i < ifs; i++) {
char ip[INET_ADDRSTRLEN];
struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr;
if (!inet_ntop(domain, &s_in->sin_addr, ip, sizeof(ip))) {
perror("inet_ntop");
return 0;
}
printf("%s - %s\n", ifr[i].ifr_name, ip);
}
close(s);
return 1;
}
int main(int argc, char *argv[])
{
int domains[] = { AF_INET, AF_INET6 };
int i;
for (i = 0; i < sizeof(domains) / sizeof(domains[0]); i++)
if (!print_addresses(domains[i]))
return 1;
return 0;
}
Your question might be imprecise but I am not sure why everyone is breaking your chops.
I think you are asking the basics in which case you probably want is getifaddrs. The man page has a little example program.
You can also get similar info using the SIOCGIFCONF option with ioctl(). There is some sample code on here and the web.
If you search around for these and similar terms you will find this question has been asked in various forms before. You have to dig around a bit.
Also note, these will not give you the public facing IP of your network if you are behind NAT.
Another way to do it in C. I do have to say though.... there are so many ways to do it from the shell, what's the point?
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <errno.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
void show_address_info( struct ifaddrs *ifa ){
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
/* ipv6 addresses have to fit in this buffer */
char buf[64];
if (AF_INET == ifa->ifa_addr->sa_family){
s4 = (struct sockaddr_in *)(ifa->ifa_addr);
if (NULL == inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s4->sin_addr), buf, sizeof(buf))){
printf("%s: inet_ntop failed!\n", ifa->ifa_name);
} else {
printf("IPv4 addr %s: %s\n", ifa->ifa_name, buf);
}
}
else if (AF_INET6 == ifa->ifa_addr->sa_family) {
s6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
if (NULL == inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))) {
printf("%s: inet_ntop failed!\n", ifa->ifa_name);
} else {
printf("IPv6 addr %s: %s\n", ifa->ifa_name, buf);
}
}
}
int main(int argc, char **argv){
struct ifaddrs *myaddrs, *ifa;
int status;
status = getifaddrs(&myaddrs);
if (status != 0){
perror("getifaddrs failed!");
exit(1);
}
for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next){
if (NULL == ifa->ifa_addr){
continue;
}
if ((ifa->ifa_flags & IFF_UP) == 0) {
continue;
}
show_address_info(ifa);
}
freeifaddrs(myaddrs);
return 0;
}
How about you simply cheat and look at the source of /sbin/ifconfig/ ? Nothing wrong with standing on the shoulders of other giants...
Not a complete solution yet, but take a look in /proc/net!
dev lists the available interface devices by name,
route lists some routes, as does ipv6_route,
arp lists devices in the actual routing table (does not include localhost).
Not as high-tech as the other solution, but it can be done with simple file reading. Linux specific, though.
You need POSIX function getaddrinfo() - it returns linked list of all IP addresses.
See man getaddrinfo for details and examples.
$ sudo ifconfig | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1
213.xx.xxx.xx
192.168.xx.x
127.0.0.1
And you can put that into popen():
/* not tested */
ph = popen("sudo ifconfig | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1", "r");
while (fgets(buf, sizeof buf, ph)) {
/* ip address, in nul-terminated string format, is in `buf` */
}
pclose(ph);
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
/*
* Who sez?
* http://blog.stackoverflow.com/2010/01/stack-overflow-where-we-hate-fun/
*/
int main(int argc, char *argv[])
{
int status;
const char * const cmd = /* die from END is too chatty */
"/sbin/ifconfig -a | \
perl -lne \
'print $1 if /inet6? addr:\\s*(\\S+)/; \
END { $. > 0 or \
warn(\"no output from ifconfig\\n\"), \
exit 1; }'";
status = system(cmd);
if (status < 0) {
perror("system");
return 1;
}
else if (status != 0) {
const char *extra;
status = WEXITSTATUS(status);
extra = status == 127 ? " (is /bin/sh ok?)" : "";
fprintf(stderr, "%s: command failed with status %d%s\n",
argv[0], status, extra);
}
return 0;
}

Resources