285 lines
9.1 KiB
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);
|
||
|
}
|
||
|
}
|