tesses.broadcast/libbroadcast/source/broadcast.c

285 lines
9.1 KiB
C

#include <tessesbroadcast.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define BROADCAST 0xFFFFFFFF //since it is a palendrome we dont need endian casts
#define ANY 0
const char* SIGReq = "TessesBcReq";
const char* SIGResp = "TessesBcResp";
bool _sleep(void* user,int times)
{
return true;
}
char* broadcast_fromconst(const char* text)
{
size_t len =strlen(text);
char* textnew = (char*)malloc(len+1);
snprintf(textnew,len,text);
return textnew;
}
typedef union {
in_addr_t addr;
uint8_t bytes[4];
} ip_t;
char* fix127(char* url,struct sockaddr_in* addr)
{
if(addr->sin_addr.s_addr == ANY)
{
return url;
}
size_t _strlen = strlen(url);
char* schemeDel=strstr(url,"://");
if(schemeDel)
{
char* hostname_begin = schemeDel+3;
if(hostname_begin > url + _strlen) return url;
char* hostname_port_end = strstr(hostname_begin,"/");
if(!hostname_port_end || hostname_port_end > url + _strlen) hostname_port_end = _strlen + url;
char* hostname_port_begin = strstr(hostname_begin,":");
if(hostname_port_begin && hostname_port_begin >= hostname_port_end) hostname_port_begin=NULL;
if(hostname_port_begin) *hostname_port_begin=0;
if(hostname_port_end) *hostname_port_end=0;
if(memcmp(hostname_begin,"127.",4) == 0)
{
*schemeDel = 0;
if(hostname_port_begin) *hostname_port_begin = ':';
if(hostname_port_end) *hostname_port_end='/';
char* right = hostname_port_begin ? hostname_port_begin : hostname_port_end ? hostname_port_end : "";
size_t url2len=strlen(right)+strlen(url)+35;
char* url2 =(char*)malloc(url2len);
ip_t ip;
ip.addr = addr->sin_addr.s_addr;
snprintf(url2,url2len,"%s://%u.%u.%u.%u%s",url,ip.bytes[0],ip.bytes[1],ip.bytes[2],ip.bytes[3],right);
free(url);
return url2;
}
else
if(strcmp(hostname_begin,"localhost") == 0)
{
*schemeDel = 0;
if(hostname_port_begin) *hostname_port_begin = ':';
if(hostname_port_end) *hostname_port_end='/';
char* right = hostname_port_begin ? hostname_port_begin : hostname_port_end ? hostname_port_end : "";
size_t url2len=strlen(right)+strlen(url)+35;
char* url2 =(char*)malloc(url2len);
ip_t ip;
ip.addr = addr->sin_addr.s_addr;
snprintf(url2,url2len,"%s://%u.%u.%u.%u%s",url,ip.bytes[0],ip.bytes[1],ip.bytes[2],ip.bytes[3],right);
free(url);
return url2;
}
else
{
if(hostname_port_begin) *hostname_port_begin = ':';
if(hostname_port_end) *hostname_port_end='/';
}
}
return url;
}
void broadcast_client(const char* serviceName,uint16_t port,void* user,broadcast_sleep_t sleepcb,broadcast_client_device_t deviceCb)
{
if(!sleepcb) sleepcb=_sleep;
size_t sigreqLen = strlen(SIGReq);
size_t sigrespLen = strlen(SIGResp);
size_t namelen = strlen(serviceName);
if(sigreqLen+2+namelen > 4096) return;
int sock = socket(AF_INET,SOCK_DGRAM,0);
int broadcast=1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
&broadcast, sizeof broadcast);
struct sockaddr_in bindAddr, bcAddr;
memset(&bindAddr,0,sizeof(bindAddr));
memset(&bcAddr,0,sizeof(bcAddr));
bindAddr.sin_family = AF_INET;
bindAddr.sin_port = 0; //get new port
bindAddr.sin_addr.s_addr = ANY;
bcAddr.sin_addr.s_addr = BROADCAST;
bcAddr.sin_family = AF_INET;
bcAddr.sin_port = htons(port);
bind(sock,(struct sockaddr*)&bindAddr,(socklen_t)sizeof(bindAddr));
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)); //timeout thanks to https://stackoverflow.com/a/13547864
uint8_t message[4096];
memcpy(message,SIGReq,sigreqLen);
message[sigreqLen] = (uint8_t)((namelen >> 8) & 0xFF);
message[sigreqLen+1] = (uint8_t)(namelen & 0xFF);
memcpy(message+sigreqLen+2,serviceName,namelen);
sendto(sock,message,sigreqLen+2+namelen,0,(struct sockaddr*)&bcAddr,(socklen_t)sizeof(bcAddr));
int times=0;
int retval;
do{
ssize_t len=0;
struct sockaddr addr;
socklen_t addrlen;
do{
errno=0;
len = recvfrom(sock,message,4096,0,&addr,&addrlen);
if(len == -1)
{
if(errno == ETIMEDOUT || errno == EAGAIN)
{
if(!sleepcb(user,times))
{
close(sock);
return;
}
}
else
{
close(sock);
return;
}
}
else
{
break;
}
}while(true);
if(len < sigrespLen+2) continue;
if(memcmp(SIGResp,message,sigrespLen) != 0) continue;
int deviceNameLen= (message[sigrespLen] << 8) | message[sigrespLen+1];
if(len<deviceNameLen+4+sigrespLen) continue;
char* name = (char*)malloc(deviceNameLen+1);
if(name == NULL) continue;
memcpy(name,message+sigrespLen+2,deviceNameLen);
name[deviceNameLen]=0;
int serviceUrlLen = (message[sigrespLen+2+deviceNameLen] << 8) | message[sigrespLen+3+deviceNameLen];
if(len<deviceNameLen+4+sigrespLen+serviceUrlLen)
{
free(name);
continue;
}
char* url = (char*)malloc(serviceUrlLen+1);
if(url == NULL) {
free(name);
continue;
}
memcpy(url,message+sigrespLen+4+deviceNameLen,serviceUrlLen);
url[serviceUrlLen]=0;
broadcast_device_t device;
device.addr = addr;
device.addrlen = addrlen;
device.name = name;
url=fix127(url,(struct sockaddr_in*)&addr);
device.url = url;
if(deviceCb)
deviceCb(&device);
free(name);
free(url);
} while(sleepcb(user,++times));
close(sock);
}
void broadcast_server(broadcast_server_cb_t cb,uint16_t port)
{
size_t sigreqLen = strlen(SIGReq);
size_t sigrespLen = strlen(SIGResp);
struct sockaddr_in bindAddr;
bindAddr.sin_addr.s_addr = ANY;
bindAddr.sin_family = AF_INET;
bindAddr.sin_port = htons(port);
int sock = socket(AF_INET,SOCK_DGRAM,0);
int broadcast=1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
&broadcast, sizeof broadcast);
bind(sock,(struct sockaddr*)&bindAddr,(socklen_t)sizeof(bindAddr));
uint8_t message[4096];
while(true)
{
struct sockaddr addr;
socklen_t addrlen;
ssize_t len = recvfrom(sock,message,4096,0,&addr,&addrlen);
if(len < sigreqLen+2) continue;
if(memcmp(SIGReq,message,sigreqLen) != 0) continue;
int serviceNameLen= (message[sigreqLen] << 8) | message[sigreqLen+1];
if(len<sigreqLen+2+serviceNameLen) return;
char* name = (char*)malloc(serviceNameLen+1);
if(name == NULL) continue;
memcpy(name,message+sigreqLen+2,serviceNameLen);
name[serviceNameLen]=0;
broadcast_server_ctx_t ctx;
ctx.toSend=false;
ctx.serviceName=name;
ctx.addr = addr;
ctx.addrlen=addrlen;
ctx.deviceName=NULL;
ctx.serviceUrl=NULL;
if(cb)
cb(&ctx);
if(ctx.toSend)
{
const char* deviceName = ctx.deviceName ? ctx.deviceName : "";
const char* serviceUrl = ctx.serviceUrl ? ctx.serviceUrl : "";
size_t deviceNameLen = strlen(deviceName);
size_t serviceUrlLen = strlen(serviceUrl);
size_t msgLen = deviceNameLen+serviceUrlLen+4+sigrespLen;
if(msgLen > 4096)
{
if(ctx.serviceUrl) free(ctx.serviceUrl);
if(ctx.deviceName) free(ctx.deviceName);
free(name);
continue;
}
memcpy(message,SIGResp,sigrespLen);
message[sigrespLen] = (uint8_t)((deviceNameLen >> 8) & 0xFF);
message[sigrespLen+1] = (uint8_t)(deviceNameLen & 0xFF);
memcpy(message+sigrespLen+2,deviceName,deviceNameLen);
message[sigrespLen+2+deviceNameLen] = (uint8_t)((serviceUrlLen >> 8) & 0xFF);
message[sigrespLen+3+deviceNameLen] = (uint8_t)(serviceUrlLen & 0xFF);
memcpy(message+sigrespLen+4+deviceNameLen,serviceUrl,serviceUrlLen);
ip_t ip;
struct sockaddr_in* addrin=(struct sockaddr_in*)&addr;
ip.addr = addrin->sin_addr.s_addr;
printf("SENDING to: %u.%u.%u.%u:%u\n",ip.bytes[0],ip.bytes[1],ip.bytes[2],ip.bytes[3],ntohs(addrin->sin_port));
sendto(sock,message,msgLen,0,&addr,addrlen);
}
if(ctx.serviceUrl) free(ctx.serviceUrl);
if(ctx.deviceName) free(ctx.deviceName);
free(name);
}
}