mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-12 15:33:23 -05:00
Initial checkin
This commit is contained in:
parent
612f2fa2fd
commit
30183d97b2
389
src/daap-proto.c
Normal file
389
src/daap-proto.c
Normal file
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* $Id$
|
||||
* Helper functions for formatting a daap message
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "daap-proto.h"
|
||||
#include "err.h"
|
||||
#include "restart.h"
|
||||
|
||||
/* Forwards */
|
||||
DAAP_BLOCK *daap_get_new(void);
|
||||
DAAP_BLOCK *daap_add_formatted(DAAP_BLOCK *parent, char *tag,
|
||||
int len, char *value);
|
||||
int daap_serialmem(DAAP_BLOCK *root, char *where);
|
||||
int daap_compress(char *input, long in_len, char *output, long *out_len);
|
||||
|
||||
|
||||
/*
|
||||
* daap_get_new
|
||||
*
|
||||
* Initialize a new daap struct
|
||||
*/
|
||||
DAAP_BLOCK *daap_get_new(void) {
|
||||
DAAP_BLOCK *pnew;
|
||||
|
||||
pnew=(DAAP_BLOCK*)malloc(sizeof(DAAP_BLOCK));
|
||||
if(!pnew) {
|
||||
DPRINTF(ERR_WARN,"Error mallocing a daap block\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pnew->value=NULL;
|
||||
pnew->parent=NULL;
|
||||
pnew->children=NULL;
|
||||
pnew->next=NULL;
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_formatted
|
||||
*
|
||||
* add a block exactly as formatted in value.
|
||||
*
|
||||
* Note that value WILL be freed later in daap_free, so
|
||||
* the value paramater must have been malloced
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_formatted(DAAP_BLOCK *parent, char *tag,
|
||||
int size, char *value) {
|
||||
DAAP_BLOCK *current,*last;
|
||||
DAAP_BLOCK *pnew;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Adding daap tag %s\n",tag);
|
||||
pnew = daap_get_new();
|
||||
if(!pnew)
|
||||
return NULL;
|
||||
|
||||
pnew->reported_size=size;
|
||||
pnew->parent=parent;
|
||||
pnew->size=size;
|
||||
memcpy(pnew->tag,tag,4);
|
||||
|
||||
if((size <= 4) && (size > 0)) { /* we can just put it in svalue */
|
||||
memcpy(pnew->svalue,value,size);
|
||||
pnew->free=0;
|
||||
} else {
|
||||
pnew->value=value;
|
||||
pnew->free=1;
|
||||
}
|
||||
pnew->next=NULL;
|
||||
|
||||
/* walk the child list and put it at the end */
|
||||
if(parent) {
|
||||
current=last=parent->children;
|
||||
while(current) {
|
||||
last=current;
|
||||
current=current->next;
|
||||
}
|
||||
|
||||
if(last) { /* some there already */
|
||||
last->next=pnew;
|
||||
} else {
|
||||
parent->children=pnew;
|
||||
}
|
||||
}
|
||||
|
||||
/* now, walk the chain and update sizes */
|
||||
current=pnew->parent;
|
||||
while(current) {
|
||||
current->reported_size += (8 + pnew->reported_size);
|
||||
current=current->parent;
|
||||
}
|
||||
|
||||
return pnew;
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_int
|
||||
*
|
||||
* Add an int block to a specific parent
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_long(DAAP_BLOCK *parent, char *tag, int v1, int v2) {
|
||||
char *ivalue;
|
||||
ivalue=(char*)malloc(8);
|
||||
if(!ivalue)
|
||||
return NULL;
|
||||
|
||||
ivalue[0]=(v1 >> 24) & 0xFF;
|
||||
ivalue[1]=(v1 >> 16) & 0xFF;
|
||||
ivalue[2]=(v1 >> 8) & 0xFF;
|
||||
ivalue[3]=v1 & 0xFF;
|
||||
|
||||
ivalue[4]=(v1 >> 24) & 0xFF;
|
||||
ivalue[5]=(v1 >> 16) & 0xFF;
|
||||
ivalue[6]=(v1 >> 8) & 0xFF;
|
||||
ivalue[7]=v1 & 0xFF;
|
||||
|
||||
return daap_add_formatted(parent,tag,8,ivalue);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_int
|
||||
*
|
||||
* Add an int block to a specific parent
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_int(DAAP_BLOCK *parent, char *tag, int value) {
|
||||
char ivalue[4];
|
||||
|
||||
ivalue[0]=(value >> 24) & 0xFF;
|
||||
ivalue[1]=(value >> 16) & 0xFF;
|
||||
ivalue[2]=(value >> 8) & 0xFF;
|
||||
ivalue[3]=value & 0xFF;
|
||||
|
||||
return daap_add_formatted(parent,tag,4,ivalue);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_short
|
||||
*
|
||||
* Add an int block to a specific parent
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_short(DAAP_BLOCK *parent, char *tag, short int value) {
|
||||
char ivalue[2];
|
||||
|
||||
ivalue[0]=(value >> 8) & 0xFF;
|
||||
ivalue[1]=value & 0xFF;
|
||||
|
||||
return daap_add_formatted(parent,tag,2,ivalue);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_char
|
||||
*
|
||||
* Add a single char
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_char(DAAP_BLOCK *parent, char *tag, char value) {
|
||||
return daap_add_formatted(parent,tag,1,&value);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_data
|
||||
*
|
||||
* Add unstructured data to a specific parent
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_data(DAAP_BLOCK *parent, char *tag,
|
||||
int len, void *value) {
|
||||
void *pvalue;
|
||||
|
||||
if(len > 4) {
|
||||
pvalue=(void*)malloc(len);
|
||||
if(!pvalue)
|
||||
return NULL;
|
||||
|
||||
memcpy(pvalue,value,len);
|
||||
|
||||
return daap_add_formatted(parent,tag,len,pvalue);
|
||||
}
|
||||
return daap_add_formatted(parent,tag,len,value);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_string
|
||||
*
|
||||
* Add a string element to a specific parent
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_string(DAAP_BLOCK *parent, char *tag, char *value) {
|
||||
char *newvalue;
|
||||
|
||||
if(strlen(value) > 4) {
|
||||
newvalue=strdup(value);
|
||||
|
||||
if(!newvalue)
|
||||
return NULL;
|
||||
|
||||
return daap_add_formatted(parent,tag,strlen(newvalue),newvalue);
|
||||
}
|
||||
return daap_add_formatted(parent,tag,strlen(value),value);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_add_empty
|
||||
*
|
||||
* add a tag whose only value is to act as an aggregator
|
||||
*/
|
||||
DAAP_BLOCK *daap_add_empty(DAAP_BLOCK *parent, char *tag) {
|
||||
return daap_add_formatted(parent,tag,0,NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_serialmem
|
||||
*
|
||||
* Serialize a daap tree to memory, so it can be
|
||||
* gzipped
|
||||
*/
|
||||
int daap_serialmem(DAAP_BLOCK *root, char *where) {
|
||||
DAAP_BLOCK *current;
|
||||
char size[4];
|
||||
|
||||
if(!root)
|
||||
return 0;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Serializing %c%c%c%c\n",root->tag[0],root->tag[1],
|
||||
root->tag[2],root->tag[3]);
|
||||
|
||||
|
||||
memcpy(where,root->tag,4);
|
||||
where+=4;
|
||||
|
||||
size[0] = (root->reported_size >> 24) & 0xFF;
|
||||
size[1] = (root->reported_size >> 16) & 0xFF;
|
||||
size[2] = (root->reported_size >> 8 ) & 0xFF;
|
||||
size[3] = (root->reported_size) & 0xFF;
|
||||
|
||||
memcpy(where,&size,4);
|
||||
where+=4;
|
||||
|
||||
if(root->size) {
|
||||
if(root->free)
|
||||
memcpy(where,root->value,root->size);
|
||||
else
|
||||
memcpy(where,root->svalue,root->size);
|
||||
|
||||
where+=root->size;
|
||||
}
|
||||
|
||||
if(daap_serialmem(root->children,where))
|
||||
return -1;
|
||||
|
||||
if(daap_serialmem(root->next,where))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* daap_serialize
|
||||
*
|
||||
* Throw the whole daap structure out a fd (depth first),
|
||||
* gzipped
|
||||
*
|
||||
* FIXME: this is gross. clean this up
|
||||
*/
|
||||
int daap_serialize(DAAP_BLOCK *root, int fd, int gzip) {
|
||||
char *uncompressed;
|
||||
long uncompressed_len;
|
||||
char *compressed;
|
||||
long compressed_len;
|
||||
int err;
|
||||
|
||||
uncompressed_len = root->reported_size + 8;
|
||||
uncompressed=(char*)malloc(uncompressed_len);
|
||||
if(!uncompressed) {
|
||||
DPRINTF(ERR_INFO,"Error allocating serialization block\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
daap_serialmem(root,uncompressed);
|
||||
|
||||
if(gzip) {
|
||||
/* guarantee enough buffer space */
|
||||
compressed_len = uncompressed_len * 101/100 + 12;
|
||||
compressed=(char*)malloc(compressed_len);
|
||||
if(!compressed) {
|
||||
DPRINTF(ERR_INFO,"Error allocation compression block\n");
|
||||
free(uncompressed);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err=daap_compress(uncompressed,uncompressed_len,
|
||||
compressed, &compressed_len);
|
||||
|
||||
if(err) {
|
||||
DPRINTF(ERR_INFO,"Error compressing: %s\n",strerror(errno));
|
||||
free(uncompressed);
|
||||
free(compressed);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(r_write(fd,compressed,compressed_len) != compressed_len) {
|
||||
DPRINTF(ERR_INFO,"Error writing compressed daap stream\n");
|
||||
free(uncompressed);
|
||||
free(compressed);
|
||||
return -1;
|
||||
}
|
||||
free(compressed);
|
||||
free(uncompressed);
|
||||
} else {
|
||||
if(r_write(fd,uncompressed,uncompressed_len) != uncompressed_len) {
|
||||
DPRINTF(ERR_INFO,"Error writing uncompressed daap stream\n");
|
||||
free(uncompressed);
|
||||
return -1;
|
||||
}
|
||||
free(uncompressed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_compress
|
||||
*
|
||||
* The zlib library is documented as threadsafe so long as
|
||||
* the zalloc and zfree routines are implemented reentrantly.
|
||||
*
|
||||
* I have no idea what platforms this will be ported to,
|
||||
* and even though I do not believe the functions I am using
|
||||
* will call zalloc or zfree, I am going to put this function
|
||||
* in a critical section. Someone with more knowledge of zlib
|
||||
* than I can determine if it is really necessary.
|
||||
*
|
||||
*/
|
||||
int daap_compress(char *input, long in_len, char *output, long *out_len) {
|
||||
int err;
|
||||
|
||||
err=compress(output,out_len,input,in_len);
|
||||
switch(err) {
|
||||
case Z_OK:
|
||||
break;
|
||||
case Z_MEM_ERROR:
|
||||
errno=ENOMEM;
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
errno=EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return (err == Z_OK ? 0 : -1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* daap_free
|
||||
*
|
||||
* Free an entire daap formatted block
|
||||
*/
|
||||
int daap_free(DAAP_BLOCK *root) {
|
||||
if((root->size)&&(root->free))
|
||||
free(root->value);
|
||||
free(root->children);
|
||||
free(root->next);
|
||||
free(root);
|
||||
return 0;
|
||||
}
|
49
src/daap-proto.h
Normal file
49
src/daap-proto.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* $Id$
|
||||
* Helper functions for formatting a daap message
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _DAAP_PROTO_H_
|
||||
#define _DAAP_PROTO_H_
|
||||
|
||||
|
||||
typedef struct daap_block_tag {
|
||||
char tag[4];
|
||||
int reported_size;
|
||||
int size;
|
||||
int free;
|
||||
char *value;
|
||||
char svalue[4]; /* for statics up to 4 bytes */
|
||||
struct daap_block_tag *parent;
|
||||
struct daap_block_tag *children;
|
||||
struct daap_block_tag *next;
|
||||
} DAAP_BLOCK;
|
||||
|
||||
DAAP_BLOCK *daap_add_int(DAAP_BLOCK *parent, char *tag, int value);
|
||||
DAAP_BLOCK *daap_add_data(DAAP_BLOCK *parent, char *tag, int len, void *value);
|
||||
DAAP_BLOCK *daap_add_string(DAAP_BLOCK *parent, char *tag, char *value);
|
||||
DAAP_BLOCK *daap_add_empty(DAAP_BLOCK *parent, char *tag);
|
||||
DAAP_BLOCK *daap_add_char(DAAP_BLOCK *parent, char *tag, char value);
|
||||
DAAP_BLOCK *daap_add_short(DAAP_BLOCK *parent, char *tag, short int value);
|
||||
DAAP_BLOCK *daap_add_long(DAAP_BLOCK *parent, char *tag, int v1, int v2);
|
||||
int daap_serialize(DAAP_BLOCK *root, int fd, int gzip);
|
||||
int daap_free(DAAP_BLOCK *root);
|
||||
|
||||
#endif
|
||||
|
300
src/daap.c
Normal file
300
src/daap.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* $Id$
|
||||
* Build daap structs for replies
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@pedde.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "daap-proto.h"
|
||||
#include "daap.h"
|
||||
#include "err.h"
|
||||
|
||||
typedef struct tag_daap_items {
|
||||
int type;
|
||||
char *tag;
|
||||
char *description;
|
||||
} DAAP_ITEMS;
|
||||
|
||||
DAAP_ITEMS taglist[] = {
|
||||
{ 0x05, "miid", "dmap.itemid" },
|
||||
{ 0x09, "minm", "dmap.itemname" },
|
||||
{ 0x01, "mikd", "dmap.itemkind" },
|
||||
{ 0x07, "mper", "dmap.persistentid" },
|
||||
{ 0x0C, "mcon", "dmap.container" },
|
||||
{ 0x05, "mcti", "dmap.containeritemid" },
|
||||
{ 0x05, "mpco", "dmap.parentcontainerid" },
|
||||
{ 0x05, "mstt", "dmap.status" },
|
||||
{ 0x09, "msts", "dmap.statusstring" },
|
||||
{ 0x05, "mimc", "dmap.itemcount" },
|
||||
{ 0x05, "mctc", "dmap.containercount" },
|
||||
{ 0x05, "mrco", "dmap.returnedcount" },
|
||||
{ 0x05, "mtco", "dmap.specifiedtotalcount" },
|
||||
{ 0x0C, "mlcl", "dmap.listing" },
|
||||
{ 0x0C, "mlit", "dmap.listingitem" },
|
||||
{ 0x0C, "mbcl", "dmap.bag" },
|
||||
{ 0x0C, "mdcl", "dmap.dictionary" },
|
||||
{ 0x0C, "msrv", "dmap.serverinforesponse" },
|
||||
{ 0x01, "msau", "dmap.authenticationmethod" },
|
||||
{ 0x01, "mslr", "dmap.loginrequired" },
|
||||
{ 0x0B, "mpro", "dmap.protocolversion" },
|
||||
{ 0x01, "msal", "dmap.supportsautologout" },
|
||||
{ 0x01, "msup", "dmap.supportsupdate" },
|
||||
{ 0x01, "mspi", "dmap.supportspersistentids" },
|
||||
{ 0x01, "msex", "dmap.supportsextensions" },
|
||||
{ 0x01, "msbr", "dmap.supportsbrowse" },
|
||||
{ 0x01, "msqy", "dmap.supportsquery" },
|
||||
{ 0x01, "msix", "dmap.supportsindex" },
|
||||
{ 0x01, "msrs", "dmap.supportsresolve" },
|
||||
{ 0x05, "mstm", "dmap.timeoutinterval" },
|
||||
{ 0x05, "msdc", "dmap.databasescount" },
|
||||
{ 0x0C, "mlog", "dmap.loginresponse" },
|
||||
{ 0x05, "mlid", "dmap.sessionid" },
|
||||
{ 0x0C, "mupd", "dmap.updateresponse" },
|
||||
{ 0x05, "musr", "dmap.serverrevision" },
|
||||
{ 0x01, "muty", "dmap.updatetype" },
|
||||
{ 0x0C, "mudl", "dmap.deletedidlisting" },
|
||||
{ 0x0C, "mccr", "dmap.contentcodesresponse" },
|
||||
{ 0x05, "mcnm", "dmap.contentcodesnumber" },
|
||||
{ 0x09, "mcna", "dmap.contentcodesname" },
|
||||
{ 0x03, "mcty", "dmap.contentcodestype" },
|
||||
{ 0x0B, "apro", "daap.protocolversion" },
|
||||
{ 0x0C, "avdb", "daap.serverdatabases" },
|
||||
{ 0x0C, "abro", "daap.databasebrowse" },
|
||||
{ 0x0C, "abal", "daap.browsealbumlisting" },
|
||||
{ 0x0C, "abar", "daap.browseartistlisting" },
|
||||
{ 0x0C, "abcp", "daap.browsecomposerlisting" },
|
||||
{ 0x0C, "abgn", "daap.browsegenrelisting" },
|
||||
{ 0x0C, "adbs", "daap.databasesongs" },
|
||||
{ 0x09, "asal", "daap.songalbum" },
|
||||
{ 0x09, "asar", "daap.songartist" },
|
||||
{ 0x03, "asbt", "daap.songbeatsperminute" },
|
||||
{ 0x03, "asbr", "daap.songbitrate" },
|
||||
{ 0x09, "ascm", "daap.songcomment" },
|
||||
{ 0x01, "asco", "daap.songcompilation" },
|
||||
{ 0x09, "ascp", "daap.songcomposer" },
|
||||
{ 0x0A, "asda", "daap.songdateadded" },
|
||||
{ 0x0A, "asdm", "daap.songdatemodified" },
|
||||
{ 0x03, "asdc", "daap.songdisccount" },
|
||||
{ 0x03, "asdn", "daap.songdiscnumber" },
|
||||
{ 0x01, "asdb", "daap.songdisabled" },
|
||||
{ 0x09, "aseq", "daap.songeqpreset" },
|
||||
{ 0x09, "asfm", "daap.songformat" },
|
||||
{ 0x09, "asgn", "daap.songgenre" },
|
||||
{ 0x09, "asdt", "daap.songdescription" },
|
||||
{ 0x02, "asrv", "daap.songrelativevolume" },
|
||||
{ 0x05, "assr", "daap.songsamplerate" },
|
||||
{ 0x05, "assz", "daap.songsize" },
|
||||
{ 0x05, "asst", "daap.songstarttime" },
|
||||
{ 0x05, "assp", "daap.songstoptime" },
|
||||
{ 0x05, "astm", "daap.songtime" },
|
||||
{ 0x03, "astc", "daap.songtrackcount" },
|
||||
{ 0x03, "astn", "daap.songtracknumber" },
|
||||
{ 0x01, "asur", "daap.songuserrating" },
|
||||
{ 0x03, "asyr", "daap.songyear" },
|
||||
{ 0x01, "asdk", "daap.songdatakind" },
|
||||
{ 0x09, "asul", "daap.songdataurl" },
|
||||
{ 0x0C, "aply", "daap.databaseplaylists" },
|
||||
{ 0x01, "abpl", "daap.baseplaylist" },
|
||||
{ 0x0C, "apso", "daap.playlistsongs" },
|
||||
{ 0x0C, "arsv", "daap.resolve" },
|
||||
{ 0x0C, "arif", "daap.resolveinfo" },
|
||||
{ 0x05, "aeNV", "com.apple.itunes.norm-volume" },
|
||||
{ 0x01, "aeSP", "com.apple.itunes.smart-playlist" },
|
||||
{ 0x00, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
int daap_add_mdcl(DAAP_BLOCK *root, char *tag, char *name, short int number) {
|
||||
DAAP_BLOCK *mdcl;
|
||||
int g=1;
|
||||
|
||||
mdcl=daap_add_empty(root,"mdcl");
|
||||
if(mdcl) {
|
||||
g=(int)daap_add_string(mdcl,"mcnm",tag);
|
||||
g = g && daap_add_string(mdcl,"mcna",name);
|
||||
g = g && daap_add_short(mdcl,"mcty",number);
|
||||
}
|
||||
|
||||
return (mdcl ? g : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_response_content_codes
|
||||
*
|
||||
* handle the daap block for the /content-codes URI
|
||||
*
|
||||
* This might more easily be done by just emitting a binary
|
||||
* of the content-codes from iTunes, since this really
|
||||
* isn't dynamic
|
||||
*/
|
||||
|
||||
DAAP_BLOCK *daap_response_content_codes(void) {
|
||||
DAAP_BLOCK *root;
|
||||
DAAP_ITEMS *current=taglist;
|
||||
int g=1;
|
||||
|
||||
|
||||
root=daap_add_empty(NULL,"mccr");
|
||||
if(root) {
|
||||
g = (int)daap_add_int(root,"mstt",200);
|
||||
|
||||
while(current->type) {
|
||||
g = g && daap_add_mdcl(root,current->tag,current->description,
|
||||
current->type);
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
if(!g) {
|
||||
daap_free(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* daap_response_login
|
||||
*
|
||||
* handle the daap block for the /login URI
|
||||
*/
|
||||
|
||||
DAAP_BLOCK *daap_response_login(void) {
|
||||
DAAP_BLOCK *root;
|
||||
int g=1;
|
||||
|
||||
|
||||
root=daap_add_empty(NULL,"mlog");
|
||||
if(root) {
|
||||
g = (int)daap_add_int(root,"mstt",200);
|
||||
g = g && daap_add_int(root,"mlid",7); /* static id! */
|
||||
}
|
||||
|
||||
if(!g) {
|
||||
daap_free(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_response_update
|
||||
*
|
||||
* handle the daap block for the /update URI
|
||||
*/
|
||||
|
||||
DAAP_BLOCK *daap_response_update(void) {
|
||||
DAAP_BLOCK *root;
|
||||
int g=1;
|
||||
|
||||
root=daap_add_empty(NULL,"mupd");
|
||||
if(root) {
|
||||
g = (int)daap_add_int(root,"mstt",200);
|
||||
/* theoretically, this would go up if the db changes? */
|
||||
g = g && daap_add_int(root,"musr",3);
|
||||
}
|
||||
|
||||
if(!g) {
|
||||
daap_free(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_response_databases
|
||||
*
|
||||
* handle the daap block for the /databases URI
|
||||
*/
|
||||
|
||||
DAAP_BLOCK *daap_response_databases(void) {
|
||||
DAAP_BLOCK *root;
|
||||
DAAP_BLOCK *mlcl;
|
||||
DAAP_BLOCK *mlit;
|
||||
int g=1;
|
||||
|
||||
root=daap_add_empty(NULL,"avdb");
|
||||
if(root) {
|
||||
g = (int)daap_add_int(root,"mstt",200);
|
||||
g = g && daap_add_char(root,"muty",0);
|
||||
g = g && daap_add_int(root,"mtco",1);
|
||||
g = g && daap_add_int(root,"mrco",1);
|
||||
mlcl=daap_add_empty(root,"mlcl");
|
||||
if(mlcl) {
|
||||
mlit=daap_add_empty(mlcl,"mlit");
|
||||
if(mlit) {
|
||||
g = g && daap_add_int(mlit,"miid",0x20);
|
||||
g = g && daap_add_long(mlit,"mper",0,1);
|
||||
g = g && daap_add_string(mlit,"minm","daapd music");
|
||||
g = g && daap_add_int(mlit,"mimc",0x10);
|
||||
g = g && daap_add_int(mlit,"mctc",0x1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g = g && mlcl && mlit;
|
||||
|
||||
if(!g) {
|
||||
DPRINTF(ERR_INFO,"Memory problem. Bailing\n");
|
||||
daap_free(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
/*
|
||||
* daap_response_server_info
|
||||
*
|
||||
* handle the daap block for the /server-info URI
|
||||
*/
|
||||
DAAP_BLOCK *daap_response_server_info(void) {
|
||||
DAAP_BLOCK *root;
|
||||
int g=1;
|
||||
|
||||
root=daap_add_empty(NULL,"msrv");
|
||||
|
||||
if(root) {
|
||||
g = (int)daap_add_int(root,"mstt",200); /* result */
|
||||
g = g && daap_add_int(root,"mpro",2 << 16); /* dmap proto ? */
|
||||
g = g && daap_add_int(root,"apro",2 << 16); /* daap protocol */
|
||||
g = g && daap_add_string(root,"minm","daapd music"); /* server name */
|
||||
g = g && daap_add_char(root,"mslr",0); /* logon required */
|
||||
g = g && daap_add_int(root,"mstm",1800); /* timeout - iTunes=1800 */
|
||||
g = g && daap_add_char(root,"msal",0); /* autologout */
|
||||
g = g && daap_add_char(root,"msup",0); /* update */
|
||||
g = g && daap_add_char(root,"mspi",0); /* persistant ids */
|
||||
g = g && daap_add_char(root,"msex",0); /* extensions */
|
||||
g = g && daap_add_char(root,"msbr",0); /* browsing */
|
||||
g = g && daap_add_char(root,"msqy",0); /* queries */
|
||||
g = g && daap_add_char(root,"msix",0); /* indexing? */
|
||||
g = g && daap_add_char(root,"msrs",0); /* resolve? req. persist id */
|
||||
g = g && daap_add_int(root,"msdc",1); /* database count */
|
||||
}
|
||||
|
||||
if(!g) {
|
||||
daap_free(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
33
src/daap.h
Normal file
33
src/daap.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* $Id$
|
||||
* Build daap structs for replies
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@pedde.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef _DAAP_H_
|
||||
#define _DAAP_H_
|
||||
|
||||
#include "daap-proto.h"
|
||||
|
||||
DAAP_BLOCK *daap_response_server_info(void);
|
||||
DAAP_BLOCK *daap_response_content_codes(void);
|
||||
DAAP_BLOCK *daap_response_login(void);
|
||||
DAAP_BLOCK *daap_response_update(void);
|
||||
DAAP_BLOCK *daap_response_databases(void);
|
||||
|
||||
#endif /* _DAAP_H_ */
|
||||
|
591
src/rend-posix.c
Normal file
591
src/rend-posix.c
Normal file
@ -0,0 +1,591 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Do the zeroconf/mdns/rendezvous (tm) thing. This is a hacked version
|
||||
* of Apple's Responder.c from the Rendezvous (tm) POSIX implementation
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_START@
|
||||
*
|
||||
* The contents of this file constitute Original Code as defined in and
|
||||
* are subject to the Apple Public Source License Version 1.2 (the
|
||||
* "License"). You may not use this file except in compliance with the
|
||||
* License. Please obtain a copy of the License at
|
||||
* http://www.apple.com/publicsource and read it before using this file.
|
||||
*
|
||||
* This Original Code and all software distributed under the License are
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
|
||||
* License for the specific language governing rights and limitations
|
||||
* under the License.
|
||||
*
|
||||
* @APPLE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*
|
||||
File: responder.c
|
||||
|
||||
Contains: Code to implement an mDNS responder on the Posix platform.
|
||||
|
||||
Written by: Quinn
|
||||
|
||||
Copyright: Copyright (c) 2002 by Apple Computer, Inc., All Rights Reserved.
|
||||
|
||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
|
||||
("Apple") in consideration of your agreement to the following terms, and your
|
||||
use, installation, modification or redistribution of this Apple software
|
||||
constitutes acceptance of these terms. If you do not agree with these terms,
|
||||
please do not use, install, modify or redistribute this Apple software.
|
||||
|
||||
In consideration of your agreement to abide by the following terms, and subject
|
||||
to these terms, Apple grants you a personal, non-exclusive license, under Apple's
|
||||
copyrights in this original Apple software (the "Apple Software"), to use,
|
||||
reproduce, modify and redistribute the Apple Software, with or without
|
||||
modifications, in source and/or binary forms; provided that if you redistribute
|
||||
the Apple Software in its entirety and without modifications, you must retain
|
||||
this notice and the following text and disclaimers in all such redistributions of
|
||||
the Apple Software. Neither the name, trademarks, service marks or logos of
|
||||
Apple Computer, Inc. may be used to endorse or promote products derived from the
|
||||
Apple Software without specific prior written permission from Apple. Except as
|
||||
expressly stated in this notice, no other rights or licenses, express or implied,
|
||||
are granted by Apple herein, including but not limited to any patent rights that
|
||||
may be infringed by your derivative works or by other works in which the Apple
|
||||
Software may be incorporated.
|
||||
|
||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
|
||||
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
|
||||
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
||||
COMBINATION WITH YOUR PRODUCTS.
|
||||
|
||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
|
||||
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Change History (most recent first):
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2003/10/30 22:41:56 ron
|
||||
Initial checkin
|
||||
|
||||
Revision 1.3 2002/09/21 20:44:53 zarzycki
|
||||
Added APSL info
|
||||
|
||||
Revision 1.2 2002/09/19 04:20:44 cheshire
|
||||
Remove high-ascii characters that confuse some systems
|
||||
|
||||
Revision 1.1 2002/09/17 06:24:35 cheshire
|
||||
First checkin
|
||||
|
||||
*/
|
||||
|
||||
#include "mdns/mDNSClientAPI.h"// Defines the interface to the client layer above
|
||||
#include "mdns/mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h> // For printf()
|
||||
#include <stdlib.h> // For exit() etc.
|
||||
#include <string.h> // For strlen() etc.
|
||||
#include <unistd.h> // For select()
|
||||
#include <errno.h> // For errno, EINTR
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "err.h"
|
||||
|
||||
#pragma mark ***** Globals
|
||||
|
||||
static mDNS mDNSStorage; // mDNS core uses this to store its globals
|
||||
static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
|
||||
|
||||
#pragma mark ***** Signals
|
||||
|
||||
static volatile mDNSBool gStopNow;
|
||||
|
||||
/* modified signal handling code - rep 21 Oct 2k3 */
|
||||
|
||||
// o SIGINT causes an orderly shutdown of the program.
|
||||
// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
|
||||
//
|
||||
// There are fatal race conditions in our signal handling, but there's not much
|
||||
// we can do about them while remaining within the Posix space. Specifically,
|
||||
// if a signal arrives after we test the globals its sets but before we call
|
||||
// select, the signal will be dropped. The user will have to send the signal
|
||||
// again. Unfortunately, Posix does not have a "sigselect" to atomically
|
||||
// modify the signal mask and start a select.
|
||||
|
||||
static void HandleSigInt(int sigraised)
|
||||
// A handler for SIGINT that causes us to break out of the
|
||||
// main event loop when the user types ^C. This has the
|
||||
// effect of quitting the program.
|
||||
{
|
||||
assert(sigraised == SIGINT);
|
||||
|
||||
DPRINTF(ERR_INFO,"SIGINT\n");
|
||||
gStopNow = mDNStrue;
|
||||
}
|
||||
|
||||
static void HandleSigQuit(int sigraised)
|
||||
// If we get a SIGQUIT the user is desperate and we
|
||||
// just call mDNS_Close directly. This is definitely
|
||||
// not safe (because it could reenter mDNS), but
|
||||
// we presume that the user has already tried the safe
|
||||
// alternatives.
|
||||
{
|
||||
assert(sigraised == SIGQUIT);
|
||||
|
||||
DPRINTF(ERR_INFO,"SIGQUIT\n");
|
||||
|
||||
mDNS_Close(&mDNSStorage);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#pragma mark ***** Parameter Checking
|
||||
|
||||
static mDNSBool CheckThatRichTextHostNameIsUsable(const char *richTextHostName)
|
||||
// Checks that richTextHostName is a reasonable host name
|
||||
// label and, if it isn't and printExplanation is true, prints
|
||||
// an explanation of why not.
|
||||
{
|
||||
mDNSBool result;
|
||||
domainlabel richLabel;
|
||||
domainlabel poorLabel;
|
||||
|
||||
result = mDNStrue;
|
||||
if (result && strlen(richTextHostName) > 63) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
if (result && richTextHostName[0] == 0) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
if (result) {
|
||||
ConvertCStringToDomainLabel(richTextHostName, &richLabel);
|
||||
ConvertUTF8PstringToRFC1034HostLabel(richLabel.c, &poorLabel);
|
||||
if (poorLabel.c[0] == 0) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType)
|
||||
// Checks that serviceType is a reasonable service type
|
||||
// label and, if it isn't and printExplanation is true, prints
|
||||
// an explanation of why not.
|
||||
{
|
||||
mDNSBool result;
|
||||
|
||||
result = mDNStrue;
|
||||
if (result && strlen(serviceType) > 63) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
if (result && serviceType[0] == 0) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static mDNSBool CheckThatServiceTextIsUsable(const char *serviceText,
|
||||
mDNSu8 *pStringList, mDNSu16 *pStringListLen)
|
||||
// Checks that serviceText is a reasonable service text record
|
||||
// and, if it isn't and printExplanation is true, prints
|
||||
// an explanation of why not. Also parse the text into
|
||||
// the packed PString buffer denoted by pStringList and
|
||||
// return the length of that buffer in *pStringListLen.
|
||||
// Note that this routine assumes that the buffer is
|
||||
// sizeof(RDataBody) bytes long.
|
||||
{
|
||||
mDNSBool result;
|
||||
size_t serviceTextLen;
|
||||
|
||||
// Note that parsing a C string into a PString list always
|
||||
// expands the data by one character, so the following
|
||||
// compare is ">=", not ">". Here's the logic:
|
||||
//
|
||||
// #1 For a string with not ^A's, the PString length is one
|
||||
// greater than the C string length because we add a length
|
||||
// byte.
|
||||
// #2 For every regular (not ^A) character you add to the C
|
||||
// string, you add a regular character to the PString list.
|
||||
// This does not affect the equivalence stated in #1.
|
||||
// #3 For every ^A you add to the C string, you add a length
|
||||
// byte to the PString list but you also eliminate the ^A,
|
||||
// which again does not affect the equivalence stated in #1.
|
||||
|
||||
result = mDNStrue;
|
||||
serviceTextLen = strlen(serviceText);
|
||||
if (result && strlen(serviceText) >= sizeof(RDataBody)) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
|
||||
// Now break the string up into PStrings delimited by ^A.
|
||||
// We know the data will fit so we can ignore buffer overrun concerns.
|
||||
// However, we still have to treat runs long than 255 characters as
|
||||
// an error.
|
||||
|
||||
if (result) {
|
||||
int lastPStringOffset;
|
||||
int i;
|
||||
int thisPStringLen;
|
||||
|
||||
// This algorithm is a little tricky. We start by copying
|
||||
// the string directly into the output buffer, shifted up by
|
||||
// one byte. We then fill in the first byte with a ^A.
|
||||
// We then walk backwards through the buffer and, for each
|
||||
// ^A that we find, we replace it with the difference between
|
||||
// its offset and the offset of the last ^A that we found
|
||||
// (ie lastPStringOffset).
|
||||
|
||||
memcpy(&pStringList[1], serviceText, serviceTextLen);
|
||||
pStringList[0] = 1;
|
||||
lastPStringOffset = serviceTextLen + 1;
|
||||
for (i = serviceTextLen; i >= 0; i--) {
|
||||
if ( pStringList[i] == 1 ) {
|
||||
thisPStringLen = (lastPStringOffset - i - 1);
|
||||
assert(thisPStringLen >= 0);
|
||||
if (thisPStringLen > 255) {
|
||||
result = mDNSfalse;
|
||||
break;
|
||||
} else {
|
||||
pStringList[i] = thisPStringLen;
|
||||
lastPStringOffset = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*pStringListLen = serviceTextLen + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static mDNSBool CheckThatPortNumberIsUsable(long portNumber)
|
||||
// Checks that portNumber is a reasonable port number
|
||||
// and, if it isn't and printExplanation is true, prints
|
||||
// an explanation of why not.
|
||||
{
|
||||
mDNSBool result;
|
||||
|
||||
result = mDNStrue;
|
||||
if (result && (portNumber <= 0 || portNumber > 65535)) {
|
||||
result = mDNSfalse;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark ***** Command Line Arguments
|
||||
|
||||
/* get rid of pidfile handling - rep - 21 Oct 2k3 */
|
||||
static const char kDefaultServiceType[] = "_http._tcp.";
|
||||
enum {
|
||||
kDefaultPortNumber = 80
|
||||
};
|
||||
|
||||
static mDNSBool gAvoidPort53 = mDNStrue;
|
||||
static const char *gRichTextHostName = "";
|
||||
static const char *gServiceType = kDefaultServiceType;
|
||||
static mDNSu8 gServiceText[sizeof(RDataBody)];
|
||||
static mDNSu16 gServiceTextLen = 0;
|
||||
static int gPortNumber = kDefaultPortNumber;
|
||||
|
||||
/*
|
||||
static void ParseArguments(int argc, char **argv)
|
||||
// Parses our command line arguments into the global variables
|
||||
// listed above.
|
||||
{
|
||||
int ch;
|
||||
|
||||
// Parse command line options using getopt.
|
||||
|
||||
do {
|
||||
ch = getopt(argc, argv, "v:rn:x:t:p:f:dP");
|
||||
if (ch != -1) {
|
||||
switch (ch) {
|
||||
break;
|
||||
case 'r':
|
||||
gAvoidPort53 = mDNSfalse;
|
||||
break;
|
||||
case 'n':
|
||||
gRichTextHostName = optarg;
|
||||
if ( ! CheckThatRichTextHostNameIsUsable(gRichTextHostName) ) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
gServiceType = optarg;
|
||||
if ( ! CheckThatServiceTypeIsUsable(gServiceType) ) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
if ( ! CheckThatServiceTextIsUsable(optarg, gServiceText, &gServiceTextLen) ) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
gPortNumber = atol(optarg);
|
||||
if ( ! CheckThatPortNumberIsUsable(gPortNumber) ) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
PrintUsage(argv);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (ch != -1);
|
||||
|
||||
// Check for any left over command line arguments.
|
||||
|
||||
if (optind != argc) {
|
||||
fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Check for inconsistency between the arguments.
|
||||
|
||||
if ( (gRichTextHostName[0] == 0) && (gServiceFile[0] == 0) ) {
|
||||
fprintf(stderr, "%s: You must specify a service to register (-n) or a service file (-f).\n", gProgramName);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#pragma mark ***** Registration
|
||||
|
||||
typedef struct PosixService PosixService;
|
||||
|
||||
struct PosixService {
|
||||
ServiceRecordSet coreServ;
|
||||
PosixService *next;
|
||||
int serviceID;
|
||||
};
|
||||
|
||||
static PosixService *gServiceList = NULL;
|
||||
|
||||
static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
|
||||
// mDNS core calls this routine to tell us about the status of
|
||||
// our registration. The appropriate action to take depends
|
||||
// entirely on the value of status.
|
||||
{
|
||||
switch (status) {
|
||||
|
||||
case mStatus_NoError:
|
||||
DPRINTF(ERR_DEBUG,"Callback: %##s Name Registered",
|
||||
thisRegistration->RR_SRV.name.c);
|
||||
// Do nothing; our name was successfully registered. We may
|
||||
// get more call backs in the future.
|
||||
break;
|
||||
|
||||
case mStatus_NameConflict:
|
||||
DPRINTF(ERR_WARN,"Callback: %##s Name Conflict",
|
||||
thisRegistration->RR_SRV.name.c);
|
||||
|
||||
// In the event of a conflict, this sample RegistrationCallback
|
||||
// just calls mDNS_RenameAndReregisterService to automatically
|
||||
// pick a new unique name for the service. For a device such as a
|
||||
// printer, this may be appropriate. For a device with a user
|
||||
// interface, and a screen, and a keyboard, the appropriate response
|
||||
// may be to prompt the user and ask them to choose a new name for
|
||||
// the service.
|
||||
//
|
||||
// Also, what do we do if mDNS_RenameAndReregisterService returns an
|
||||
// error. Right now I have no place to send that error to.
|
||||
|
||||
status = mDNS_RenameAndReregisterService(m, thisRegistration);
|
||||
assert(status == mStatus_NoError);
|
||||
break;
|
||||
|
||||
case mStatus_MemFree:
|
||||
DPRINTF(ERR_WARN,"Callback: %##s Memory Free",
|
||||
thisRegistration->RR_SRV.name.c);
|
||||
|
||||
// When debugging is enabled, make sure that thisRegistration
|
||||
// is not on our gServiceList.
|
||||
|
||||
#if defined(DEBUG)
|
||||
{
|
||||
PosixService *cursor;
|
||||
|
||||
cursor = gServiceList;
|
||||
while (cursor != NULL) {
|
||||
assert(&cursor->coreServ != thisRegistration);
|
||||
cursor = cursor->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free(thisRegistration);
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTF(ERR_WARN,"Callback: %##s Unknown Status %d",
|
||||
thisRegistration->RR_SRV.name.c, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int gServiceID = 0;
|
||||
|
||||
static mStatus RegisterOneService(const char * richTextHostName,
|
||||
const char * serviceType,
|
||||
const mDNSu8 text[],
|
||||
mDNSu16 textLen,
|
||||
long portNumber)
|
||||
{
|
||||
mStatus status;
|
||||
PosixService * thisServ;
|
||||
mDNSOpaque16 port;
|
||||
domainlabel name;
|
||||
domainname type;
|
||||
domainname domain;
|
||||
|
||||
status = mStatus_NoError;
|
||||
thisServ = (PosixService *) malloc(sizeof(*thisServ));
|
||||
if (thisServ == NULL) {
|
||||
status = mStatus_NoMemoryErr;
|
||||
}
|
||||
if (status == mStatus_NoError) {
|
||||
ConvertCStringToDomainLabel(richTextHostName, &name);
|
||||
ConvertCStringToDomainName(serviceType, &type);
|
||||
ConvertCStringToDomainName("local.", &domain);
|
||||
port.b[0] = (portNumber >> 8) & 0x0FF;
|
||||
port.b[1] = (portNumber >> 0) & 0x0FF;;
|
||||
status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
|
||||
&name, &type, &domain,
|
||||
NULL,
|
||||
port,
|
||||
text, textLen,
|
||||
RegistrationCallback, thisServ);
|
||||
}
|
||||
if (status == mStatus_NoError) {
|
||||
thisServ->serviceID = gServiceID;
|
||||
gServiceID += 1;
|
||||
|
||||
thisServ->next = gServiceList;
|
||||
gServiceList = thisServ;
|
||||
|
||||
DPRINTF(ERR_DEBUG,
|
||||
"Registered service %d, name '%s', type '%s', port %ld\n",
|
||||
thisServ->serviceID,
|
||||
richTextHostName,
|
||||
serviceType,
|
||||
portNumber);
|
||||
} else {
|
||||
if (thisServ != NULL) {
|
||||
free(thisServ);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void DeregisterOurServices(void)
|
||||
{
|
||||
PosixService *thisServ;
|
||||
int thisServID;
|
||||
|
||||
while (gServiceList != NULL) {
|
||||
thisServ = gServiceList;
|
||||
gServiceList = thisServ->next;
|
||||
|
||||
thisServID = thisServ->serviceID;
|
||||
|
||||
mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Deregistered service %d\n",
|
||||
thisServ->serviceID);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark **** Main
|
||||
|
||||
int rend_init(pid_t *pid) {
|
||||
mStatus status;
|
||||
mDNSBool result;
|
||||
|
||||
status = mDNS_Init(&mDNSStorage, &PlatformStorage,
|
||||
mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
|
||||
mDNS_Init_AdvertiseLocalAddresses,
|
||||
mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
|
||||
|
||||
if (status != mStatus_NoError) {
|
||||
DPRINTF(ERR_FATAL,"mDNS Error %d\n",status);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
*pid=fork();
|
||||
if(*pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Registering tcp service\n");
|
||||
RegisterOneService("mt-daapd","_http._tcp",NULL,0,3689);
|
||||
RegisterOneService("mt-daapd","_daap._tcp",NULL,0,3689);
|
||||
|
||||
signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
|
||||
signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
|
||||
|
||||
while (!gStopNow) {
|
||||
int nfds = 0;
|
||||
fd_set readfds;
|
||||
struct timeval timeout;
|
||||
int result;
|
||||
|
||||
// 1. Set up the fd_set as usual here.
|
||||
// This example client has no file descriptors of its own,
|
||||
// but a real application would call FD_SET to add them to the set here
|
||||
FD_ZERO(&readfds);
|
||||
|
||||
// 2. Set up the timeout.
|
||||
// This example client has no other work it needs to be doing,
|
||||
// so we set an effectively infinite timeout
|
||||
timeout.tv_sec = 0x3FFFFFFF;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
// 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
|
||||
mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
|
||||
|
||||
// 4. Call select as normal
|
||||
DPRINTF(ERR_DEBUG,"select(%d, %d.%06d)\n", nfds,
|
||||
timeout.tv_sec, timeout.tv_usec);
|
||||
|
||||
result = select(nfds, &readfds, NULL, NULL, &timeout);
|
||||
|
||||
if (result < 0) {
|
||||
DPRINTF(ERR_WARN,"select() returned %d errno %d\n", result, errno);
|
||||
if (errno != EINTR) gStopNow = mDNStrue;
|
||||
} else {
|
||||
// 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
|
||||
mDNSPosixProcessFDSet(&mDNSStorage, result, &readfds);
|
||||
|
||||
// 6. This example client has no other work it needs to be doing,
|
||||
// but a real client would do its work here
|
||||
// ... (do work) ...
|
||||
}
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Exiting");
|
||||
|
||||
DeregisterOurServices();
|
||||
mDNS_Close(&mDNSStorage);
|
||||
|
||||
if (status == mStatus_NoError) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = 2;
|
||||
}
|
||||
DPRINTF(ERR_DEBUG, "Finished with status %ld, result %d\n",
|
||||
status, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
11
src/rend.h
Normal file
11
src/rend.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _REND_H_
|
||||
#define _REND_H_
|
||||
|
||||
int rend_init(pid_t *pid);
|
||||
|
||||
#endif /* _REND_H_ */
|
Loading…
Reference in New Issue
Block a user