5
* Karl Kleinpaste, May 2014
7
* All files related to implementation of BibleSync, including program
8
* source, READMEs, manual pages, and related similar documents, are in
9
* the public domain. As a matter of simple decency, your social
10
* obligations are to credit the source and to coordinate any changes you
11
* make back to the origin repository. These obligations are non-
12
* binding for public domain software, but they are to be seriously
13
* handled nonetheless.
16
#ifndef __BIBLESYNC_HH__
17
#define __BIBLESYNC_HH__
19
// XIPHOS-SPECIFIC - THIS #INCLUDE TO BE REMOVED WHEN
20
// BIBLESYNC IS EXTRICATED TO AN INDEPENDENT LIBRARY.
21
#include <glib/gi18n.h>
23
#include "biblesync-version.hh"
26
// Bible Sync Protocol.
27
// http://biblesyncprotocol.wikispaces.com/
29
// BSP provides a classroom type of arrangement, where one person
30
// (speaker) is in charge of inducing others' (audience) Bible
31
// software to navigate as speaker requires. The speaker only
32
// xmits and the audience only recvs.
33
// BSP also provides a personal mode which both xmits and recvs, where
34
// one user works with multiple programs across several devices,
35
// also suitable for small teams working together, such as translators.
36
// BSP is implemented using multicast UDP with small packets in a
37
// simple format employing a few bytes of packet control followed by a
38
// series of newline-terminated "name=value" pairs.
40
// * Application interface *
43
// BibleSync *YourBibleSyncObjectPtr = new BibleSync(app, version, user);
44
// create exactly one.
45
// identify the application, its version, and the user.
48
// setMode(BSP_MODE_xyz, your_void_nav_func, "passphrase");
49
// invoke a mode, including net.setup as needed.
50
// xyz = { DISABLE, PERSONAL, SPEAKER, AUDIENCE }.
51
// DISABLE kills it, shuts off network access.
52
// PERSONAL is bidirectional.
53
// SPEAKER xmits only.
54
// AUDIENCE recvs only.
55
// => empty passphrase ("") means re-use existing passphrase.
56
// => for any active mode, the application must then start polling using
57
// the receiver, BibleSync::Receive(), and stop polling when mode
58
// goes to DISABLE. if Receive() is called while disabled, it will
59
// return FALSE to indicate its polled use should stop, otherwise TRUE.
60
// => interface for your_void_nav_func:
62
// string bible, string ref, string alt,
63
// string group, string domain,
64
// string info, string dump)
65
// there are 6 your_void_nav_func() use cases, identified in cmd:
66
// 1. 'E' (error) for network errors & malformed packets.
67
// only info + dump are useful.
68
// 2. 'M' (mismatch) against passphrase or mode or listen status.
69
// info == "announce" or "sync" or "beacon" (+ user @ [ipaddr])
70
// sync: bible, ref, alt, group, domain as arrived.
71
// announce: presence message in alt.
72
// also, individual elements are also available:
73
// overload: bible ref group domain
74
// user [ipaddr] app+ver device
77
// presence message in alt. dump available.
78
// 4. 'N' (navigation)
79
// bible, ref, alt, group, domain as arrived.
80
// info + dump available.
81
// 5. 'S' (new speaker)
82
// param overload as above. alt == sender UUID.
83
// alt is the SPEAKER-KEY. see listenToSpeaker().
84
// 6. 'D' (dead speaker)
85
// opposite of new speaker. only param is alt == UUID.
87
// - get current mode.
88
// BibleSync_mode getMode().
90
// - get current passphrase, for default use when setting a new one.
91
// string getPassphrase().
93
// - receive navigation.
94
// BibleSync::Receive(YourBibleSyncObjPtr); // *-* poll often *-*
95
// see note below; calls your_void_nav_func().
98
// BibleSync_xmit_status retval =
100
// "NASB", "John.3.16", "some alt ref",
101
// "1", "BIBLE-VERSE");
102
// params: type (sync only), bible, ref, alt-ref, group, domain.
103
// all params have defaults.
104
// => it is the application's responsibility to send well-formed verse
107
// - set self as private
108
// bool setPrivate(boolean);
109
// sets outgoing TTL to zero so no one hears you off-machine.
111
// - allow another speaker to drive us
112
// void listenToSpeaker(bool listen, string speakerkey)
113
// say yes/no to listening.
114
// speakerkey was given during nav_func 'S'.
116
// Receive() USAGE NOTE:
117
// the application must call BibleSync::Receive(YourBibleSyncObjPtr)
118
// frequently. For example:
119
// g_timeout_add(2000, // 2sec in msec.
120
// (GSourceFunc)BibleSync::Receive,
121
// biblesyncobj); // of type (BibleSync *).
122
// g_timeout_add is a glib function for polled function calls.
123
// this will induce timed-interval calls to the function, as long
124
// as the function returns TRUE; upon returning FALSE, calls stop.
125
// other than with glib, accomplish the same thing somehow else.
126
// Receive is a static method accessible from C or C++. it must be
127
// called with the pointer to your BibleSync object in order that
128
// object context be re-entered. the internal receive routine
131
// Note on speaker beacons:
132
// Protocol operates using periodic (10sec) beacons of speaker availability.
133
// By default, audience accepts listening to the first available speaker,
134
// thereafter ignores any more, but includes them in the list of available
135
// speakers and notifies the app of their availability (see above, 'S'/'D').
136
// Override this behavior choice however wished, using listenToSpeaker() in
137
// reaction to 'S' events or on user request.
138
// Speakers who stop xmitting beacons timeout, are declared dead, and
139
// removed after 30sec beacon silence, with app notification ('D').
140
// Observe that pure Speaker clears the speaker list and by default ignores
141
// all newly-identified claimants to speaker status. Again, this is default
142
// behavior, but it makes no sense to try to listen to one as the mode is
144
// Note also that Personal is both speaker and audience.
156
#include <sys/types.h>
161
#include <sys/utsname.h>
162
#include <arpa/inet.h>
163
#include <netinet/in.h>
164
#include <sys/socket.h>
165
#include <uuid/uuid.h>
168
#include <winsock2.h>
170
#include <ws2tcpip.h>
173
typedef enum _BibleSync_mode {
181
typedef enum _BibleSync_xmit_status {
186
BSP_XMIT_NO_AUDIENCE_XMIT,
189
} BibleSync_xmit_status;
191
// args: cmd, bible, verse, alt, group, domain, info, dump.
192
typedef void (*BibleSync_navigate)(char,
193
string, string, string,
203
#define min(a,b) (((a) < (b)) ? (a) : (b))
206
// message structure constants
207
#define BSP_MULTICAST "239.225.27.227"
208
#define BSP_PORT 22272
209
#define BSP_MAX_SIZE 1280 // in spec, it's 512. experimenting.
210
#define BSP_RES_SIZE 6 // unused bytes, fill out header to 32.
211
#define BSP_HEADER_SIZE 32
212
#define BSP_MAX_PAYLOAD (BSP_MAX_SIZE - BSP_HEADER_SIZE)
214
// message indications
215
#define BSP_MAGIC htonl(0x409CAF11)
216
#define BSP_PROTOCOL 2
219
#define BSP_ANNOUNCE 1 // presence announcement.
220
#define BSP_SYNC 2 // navigation synchronization.
221
#define BSP_BEACON 3 // speaker availability beacon.
222
// beacon packet is identical to presence announcement except for type.
225
#define BSP_BEACON_COUNT 10 // xmit every N calls of Receive().
226
#define BSP_BEACON_MULTIPLIER 3 // multiplier for aging to death.
228
// message content names.
229
#define BSP_APP_NAME "app.name" // req'd
230
#define BSP_APP_VERSION "app.version" // opt
231
#define BSP_APP_INSTANCE_UUID "app.inst.uuid" // req'd
232
#define BSP_APP_OS "app.os" // opt
233
#define BSP_APP_DEVICE "app.device" // opt
234
#define BSP_APP_USER "app.user" // req'd
235
#define BSP_MSG_SYNC_DOMAIN "msg.sync.domain" // req'd
236
#define BSP_MSG_SYNC_VERSE "msg.sync.verse" // req'd
237
#define BSP_MSG_SYNC_ALTVERSE "msg.sync.altVerse" // opt
238
#define BSP_MSG_SYNC_BIBLEABBREV "msg.sync.bibleAbbrev" // req'd
239
#define BSP_MSG_SYNC_GROUP "msg.sync.group" // req'd
240
#define BSP_MSG_PASSPHRASE "msg.sync.passPhrase" // req'd
242
// required number of fields to send (out) or verify (in).
243
#define BSP_FIELDS_RECV_ANNOUNCE 4
244
#define BSP_FIELDS_RECV_SYNC 8
245
#define BSP_FIELDS_XMIT_ANNOUNCE 7
246
#define BSP_FIELDS_XMIT_SYNC 12
249
# define BSP_OS "Linux"
252
# define BSP_OS "Windows"
254
# define BSP_OS "UNIX"
258
#define BSP_UUID_PRINT_LENGTH 37 // actually 36, plus '\0'.
264
// simple name/value pairs.
265
typedef std::map < string, string > BibleSyncContent;
267
typedef struct _BibleSyncMessage {
271
uint16_t num_packets;
272
uint16_t index_packet;
273
uint8_t reserved[BSP_RES_SIZE];
275
char body[BSP_MAX_PAYLOAD+1]; // +1 for stuffing '\0'.
278
typedef struct _BibleSyncSpeaker {
279
bool listen; // nav for this guy?
280
uint8_t countdown; // lifetime aging.
281
string addr; // for spoof check.
284
// key string is origin uuid.
285
typedef std::map < string, BibleSyncSpeaker > BibleSyncSpeakerMap;
286
typedef BibleSyncSpeakerMap::iterator BibleSyncSpeakerMapIterator;
288
// self identification.
289
string BibleSync_version;
291
// application identifiers.
297
// currently processing received navigation.
298
// prevents use of Transmit.
301
// when xmit-capable, we xmit BSP_BEACON every N calls of Receive().
302
uint8_t beacon_countdown;
304
// track currently-known speaker set.
305
BibleSyncSpeakerMap speakers;
307
// what operational mode we're in.
310
// callback by which Receive induces navigation.
311
BibleSync_navigate nav_func;
317
struct sockaddr_in server, client;
318
int server_fd, client_fd;
319
struct ip_mreq multicast_req;
321
// default address discoverer, for multicast configuration.
322
void InterfaceAddress();
323
struct in_addr interface_addr;
325
// unique identification.
327
char uuid_string[BSP_UUID_PRINT_LENGTH]; // printable
329
// socket init and listener start, called from setMode().
332
// dispose of network access.
336
int ReceiveInternal(); // C++ object context.
337
int InitSelectRead(char *, struct sockaddr_in *, BibleSyncMessage *);
339
// speaker list management.
341
void clearSpeakers();
344
void uuid_dump(uuid_t &u, char *destination);
345
char uuid_dump_string[BSP_UUID_PRINT_LENGTH];
346
void uuid_gen(uuid_t &u); // differentiates linux/win32.
349
// network self-analysis, borrowed from the net.
350
int get_default_if_name(char *name, socklen_t size);
351
int parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo);
352
int readNlSock(int sockFd, char *bufPtr, size_t buf_size,
353
unsigned int seqNum, unsigned int pId);
355
// no other support routines needed for Windows/Solaris/BSD.
359
BibleSync(string a, string v, string u);
363
BibleSync_mode setMode(BibleSync_mode m,
364
BibleSync_navigate n = NULL,
366
inline BibleSync_mode getMode(void) { return mode; };
368
// library identification.
369
inline string getVersion(void) { return BibleSync_version; };
371
// obtain passphrase, for default choice.
372
inline string getPassphrase(void) { return passphrase; };
375
static int Receive(void *myself); // assume C context: poll from timeout.
377
// speaker transmitter
378
BibleSync_xmit_status Transmit(char message_type = BSP_SYNC,
379
string bible = "KJV",
380
string ref = "Gen.1.1",
383
string domain = "BIBLE-VERSE");
385
// set privacy using TTL 0 in personal mode.
386
bool setPrivate(bool privacy);
388
// say whether you want to hear from this speaker.
389
void listenToSpeaker(bool listen, string speakerkey);
392
#endif // __BIBLESYNC_HH__