summaryrefslogtreecommitdiff
path: root/comm/Socket.cc
blob: 1d5a542759ce61a14c112419d7dd0ddf27351f80 (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
/*
 *	The Unix Channel
 *
 *	by Michel Beaudouin-Lafon
 *
 *	Copyright 1990-1993
 *	Laboratoire de Recherche en Informatique (LRI)
 *
 *	Sockets
 *
 *	$Id$
 *	$CurLog$
 */

#include "Socket.h"

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

extern	int	errno;

/*?class UchSocket
The class \typ{UchSocket} derives from \typ{UchChannel}.
It is a virtual base class: no objects of class \typ{UchSocket} are ever created.
It implements a Unix socket, that is a file descriptor and 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.

Addresses are always referenced by pointers.
Smart pointers to addresses (\typ{^{pUchAddress}}) can be used anywhere a pointer is used.
?*/

/*?
Construct a closed socket.
?*/
UchSocket :: UchSocket ()
: UchChannel (),
  BAddr (0),
  CAddr (0),
  AddrFamily (AF_UNSPEC)
{
}

/*?
Construct a closed socket bound to address \var{bound} and connected to address \var{connected}.
Each or both arguments may be 0.
?*/
UchSocket :: UchSocket (UchAddress* bound, UchAddress* connected)
: UchChannel (),
  BAddr (bound),
  CAddr (connected),
  AddrFamily (AF_UNSPEC)
{
}

/*?nodoc?*/
UchSocket :: UchSocket (const UchSocket& s)
: UchChannel (s),
  BAddr (s.BAddr),
  CAddr (s.CAddr)
{
}

/*?nodoc?*/
UchSocket :: ~UchSocket ()
{
	// will unreference addresses and destroy them if necessary
	BAddr = 0;
	CAddr = 0;
}

/*?nodoc?*/
UchChannel*
UchSocket :: Copy () const
{
	return new UchSocket (*this);
}

/*?
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
UchSocket :: SockType ()
{
	return SOCK_UNSPEC;
}

/*?nextdoc?*/
void
UchSocket :: BindTo (UchAddress* a)
{
	BAddr = a;
}

/*?
Set the address a socket is to be bound to or connected to.
?*/
void
UchSocket :: ConnectTo (UchAddress* a)
{
	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
UchSocket :: Open ()
{
	errno = 0;
	if (FilDes () >= 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;
	UchChannel::Open (fd);
	return TRUE;
}

/*?nextdoc?*/
int
UchSocket :: Bind ()
{
	if (! BAddr || ! BAddr->IsValid ())
		return -1;
	if (! Open ())
		return -1;
	int ret = bind (FilDes (), BAddr->GetSockAddr (), BAddr->Length ());
	if (ret < 0)
		return ret;
	
	GEN_ADDR addr;
	int alen = sizeof (addr);
	if (getsockname (FilDes (), &addr.sa, &alen) < 0)
		return -1;
	BAddr = UchAddress::Decode (&addr, alen);
	return ret;
}

/*?nextdoc?*/
int
UchSocket :: Bind (UchAddress* addr)
{
	if (addr)
		BAddr = addr;
	return Bind ();
}

/*?nextdoc?*/
int
UchSocket :: Connect ()
{
	if (! CAddr || ! CAddr->IsValid ())
		return -1;
	if (! Open ())
		return -1;
	int ret = connect (FilDes (), CAddr->GetSockAddr (), CAddr->Length ());
	if (ret < 0)
		return ret;
	
	GEN_ADDR addr;
	int alen = sizeof (addr);
	if (getpeername (FilDes (), &addr.sa, &alen) < 0)
		return -1;
	CAddr = UchAddress::Decode (&addr, 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
UchSocket :: Connect (UchAddress* addr)
{
	if (addr)
		CAddr = addr;
	return Connect ();
}

/*?
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
UchSocket :: Setup ()
{
	if (! Open ())
		return FALSE;
	if (BAddr)
		if (BAddr->IsValid ())
			if (Bind () < 0)
				return FALSE;
	if (CAddr)
		if (CAddr->IsValid ())
			if (Connect () < 0)
				return FALSE;
	return TRUE;
}

/*?nodoc?*/
char*
UchSocket :: StrRepr (char* buf)
{
	UchChannel :: 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
UchSocket :: 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
UchSocket :: SetFamily (int f)
{ }

/*?nextdoc?*/
UchAddress*
UchSocket :: BoundTo ()
{ }

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

#endif /* DOC */