summaryrefslogtreecommitdiff
path: root/comm/Socket.cc
blob: 66a16bb337ec787f983d86c55f8c24599810d0f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/*
 *	Ivy League
 *
 *	Sockets
 *
 *	Copyright 1990-2000
 *	Laboratoire de Recherche en Informatique (LRI)
 *	Centre d'Etudes de la Navigation Aerienne (CENA)
 *
 *	original code by Michel Beaudouin-Lafon,
 *	modified by Stephane Chatty and Stephane Sire
 *
 *	$Id$
 *
 */

#include "Socket.h"

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
extern int errno;

/*?class IvlSocket
The class \typ{IvlSocket} derives from \typ{IvlChannel}.  It is a
virtual base class: no objects of class \typ{IvlSocket} are ever
created.  It implements Unix sockets, that is file descriptors with
two addresses: the address the socket is bound to, and the address it
is connected to.  An address needs to be bound to a socket only if
another process wants to connect to this socket; a socket needs to be
connected to an address only for streams or connected datagrams.
Thus, none of the addresses is mandatory.

A socket can have potentially two addresses (the one it is BoundTo and
the one it is ConnectedTo. Once given to a socket, an \typ{IvlAddress}
instance pointer is no more usable because it can be deleted and
reallocated by the Socket when it is bound/connected. These address
pointers are also deleted by the socket destructor. Thus, to know the
address of a socket always use \fun{BoundTo} and \fun{ConnectedTo}.
?*/

/*?
Construct a closed socket bound to address \var{bound} and connected
to address \var{connected}. Each or both arguments may be 0.  These
addresses have to be freshly allocated objects and will
be deleted by the socket's destructor so it's not
necessary (and dangerous) to delete them.
always allocated by new. Theses addresses are deleted and
reinstanciated after a \fun{Bind}. To obtain the addresses of a
socket, use \fun{BoundTo} and \fun{ConnectedTo}.
?*/
IvlSocket :: IvlSocket (IvlAddress* bound, IvlAddress* connected)
: IvlChannel (),
  BAddr (bound),
  CAddr (connected),
  AddrFamily (AF_UNSPEC),
  Ready (false)
{
}

/*?nodoc?*/
IvlSocket :: IvlSocket (const IvlSocket& s)
: IvlChannel (s),
  BAddr (s.BAddr),
  CAddr (s.CAddr),
  Ready (s.Ready)
{
}

/*?nodoc?*/
IvlSocket :: ~IvlSocket ()
{
	if (BAddr) delete BAddr;
	if (CAddr) delete CAddr;
}

#ifdef DOC
/*?
This virtual function returns the type of the socket:
one of \var{SOCK\_UNSPEC}, \var{SOCK\_STREAM}, \var{SOCK\_DGRAM},
depending on the class.
?*/
int
IvlSocket :: SockType ()
{
}
#endif

/*?nextdoc?*/
void
IvlSocket :: BindTo (IvlAddress* a)
{
	if (BAddr) 
		delete BAddr;
	BAddr = a;
}

/*?
Set the address a socket is to be bound to or connected to.
?*/
void
IvlSocket :: ConnectTo (IvlAddress* a)
{
	if (CAddr)
		delete CAddr;
	CAddr = a;
}

/*?
Open the socket (with the \fun{socket} system call) if it is not already open.
The socket must have an address bound or connected to get the protocol family,
or its family must have been defined with \fun{SetFamily}.
Return false if the family is undefined or if a system error occurred.
?*/
bool
IvlSocket :: Open ()
{
	errno = 0;
	if (Fd >= 0)
		return true;
	if (AddrFamily == AF_UNSPEC) {
		if (BAddr)
			AddrFamily = BAddr->Family ();
		else if (CAddr)
			AddrFamily = CAddr->Family ();
		else
			return false;
	}
	int fd = socket (AddrFamily, SockType (), 0);
	if (fd < 0)
		return false;
	IvlChannel::Open (fd);
	return true;
}

/*?nextdoc?*/
int
IvlSocket :: Bind (IvlAddress* addr)
{
	if (addr)
		BindTo (addr);
	if (! BAddr || ! BAddr->IsValid ())
		return -1;
	if (! Open ())
		return -1;

	int ret = bind (Fd, BAddr->GetSockAddr (), BAddr->Length ());
	if (ret < 0)
		return ret;

	/* retrieve actual address that was allocated. This is useful if
	   ANYADDR was passed and we need to know the port number */
	GEN_ADDR naddr;
	socklen_t alen = sizeof (naddr);
	if (getsockname (Fd, &naddr.sa, &alen) < 0)
		return -1;
	BindTo (IvlAddress::Decode (&naddr, alen));

	return ret;
}

/*?
These two functions implement the Unix system calls \fun{bind} and \fun{connect}.
If \var{addr} is given, it is first associated to the socket.
Then the socket is opened, and finally the system call is performed.
The returned value is that of the system call, unless opening failed in which case
-1 is returned.
?*/
int
IvlSocket :: Connect (IvlAddress* addr)
{
	if (addr)
		ConnectTo (addr);
	if (! CAddr || ! CAddr->IsValid ())
		return -1;
	if (! Open ())
		return -1;
	int ret = connect (Fd, CAddr->GetSockAddr (), CAddr->Length ());
	if (ret < 0)
		return ret;

	/* retrieve actual address that was allocated. This is useful if
	   ANYADDR was passed and we need to know the port number */
	GEN_ADDR naddr;
	socklen_t alen = sizeof (naddr);
	if (getpeername (Fd, &naddr.sa, &alen) < 0)
		return -1;
	ConnectTo (IvlAddress::Decode (&naddr, alen));

	return ret;
}

/*?
Open the socket if it is not already open.
Bind and connect it depending on the addresses that are defined.
You should use this function instead of calling \fun{Open}, \fun{Bind} and
\fun{Connect}, for it is simpler.
This function returns false if a system error occurred. In this case, the caller can
call \fun{SysError} to report the error.
?*/
bool
IvlSocket :: Setup ()
{
	if (! Open ())
		return Ready = false;

	if (BAddr && BAddr->IsValid ())
		if (Bind () < 0)
			return  Ready = false;

	if (CAddr && CAddr->IsValid ())
		if (Connect () < 0)
			return Ready = false;
	return Ready = true;
}

/*?
This function is intended for sockets that accept connections.
It returns the file descriptor of the new connection, or -1 in case of failure.
?*/
int
IvlSocket :: Accept ()
{
	errno = 0;
       	return accept (Fd, 0, 0);
}

bool
IvlSocket :: ReuseAddress (bool on)
{
	int parm = on;
	int res = setsockopt (Fd, SOL_SOCKET, SO_REUSEADDR, (char *)&parm, sizeof (parm));
	return res < 0 ? false : true;
}

bool
IvlSocket :: AllowBroadcast (bool on)
{
	int parm = on;
	int res = setsockopt (Fd, SOL_SOCKET, SO_BROADCAST, (char *)&parm, sizeof (parm));
	return res < 0 ? false : true;
}

/*?nodoc?*/
char*
IvlSocket :: StrRepr (char* buf)
{
	IvlChannel :: StrRepr (buf);
	strcat (buf, " / ");
	if (BAddr)
		BAddr->StrRepr (buf + strlen (buf));
	else
		strcat (buf, "null");
	strcat (buf, " / ");
	if (CAddr)
		CAddr->StrRepr (buf + strlen (buf));
	else
		strcat (buf, "null");
	return buf;
}

#ifdef DOC

/*?nextdoc?*/
int
IvlSocket :: Family ()
{ }

/*?
These function get and set the family.
The possible values currently are \var{AF\_UNSPEC}, \var{AF\_UNIX} and \var{AF\_INET}.
Other values may be defined if supported by the system.
The family must be defined for \fun{Open} (and thus \fun{Setup}) to succeed.
If an address is bound or connected to the socket, its family is used.
Thus, an application seldom needs to call \fun{SetFamily}.
?*/
void
IvlSocket :: SetFamily (int f)
{ }

/*?nextdoc?*/
IvlAddress*
IvlSocket :: BoundTo ()
{ }

/*?
Return the address currently bound or connected to the socket.
?*/
IvlAddress*
IvlSocket :: ConnectedTo ()
{ }

#endif /* DOC */