Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
ccan
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
mirror
ccan
Commits
158de63f
Commit
158de63f
authored
Oct 02, 2013
by
Rusty Russell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
net: add async operation helpers.
Signed-off-by:
Rusty Russell
<
rusty@rustcorp.com.au
>
parent
3e9d2361
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
184 additions
and
77 deletions
+184
-77
ccan/net/_info
ccan/net/_info
+1
-0
ccan/net/net.c
ccan/net/net.c
+117
-77
ccan/net/net.h
ccan/net/net.h
+66
-0
No files found.
ccan/net/_info
View file @
158de63f
...
...
@@ -66,6 +66,7 @@ int main(int argc, char *argv[])
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/noerr\n");
return 0;
}
...
...
ccan/net/net.c
View file @
158de63f
/* Licensed under BSD-MIT - see LICENSE file for details */
#include <ccan/net/net.h>
#include <ccan/noerr/noerr.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
...
...
@@ -11,6 +12,7 @@
#include <errno.h>
#include <stdbool.h>
#include <netinet/in.h>
#include <assert.h>
struct
addrinfo
*
net_client_lookup
(
const
char
*
hostname
,
const
char
*
service
,
...
...
@@ -48,107 +50,145 @@ static bool set_nonblock(int fd, bool nonblock)
return
(
fcntl
(
fd
,
F_SETFL
,
flags
)
==
0
);
}
/* We only handle IPv4 and IPv6 */
#define MAX_PROTOS 2
static
void
remove_fd
(
struct
pollfd
pfd
[],
const
struct
addrinfo
*
addr
[],
socklen_t
slen
[],
unsigned
int
*
num
,
unsigned
int
i
)
static
int
start_connect
(
const
struct
addrinfo
*
addr
,
bool
*
immediate
)
{
memmove
(
pfd
+
i
,
pfd
+
i
+
1
,
(
*
num
-
i
-
1
)
*
sizeof
(
pfd
[
0
]));
memmove
(
addr
+
i
,
addr
+
i
+
1
,
(
*
num
-
i
-
1
)
*
sizeof
(
addr
[
0
]));
memmove
(
slen
+
i
,
slen
+
i
+
1
,
(
*
num
-
i
-
1
)
*
sizeof
(
slen
[
0
]));
(
*
num
)
--
;
int
fd
;
*
immediate
=
false
;
fd
=
socket
(
addr
->
ai_family
,
addr
->
ai_socktype
,
addr
->
ai_protocol
);
if
(
fd
==
-
1
)
return
fd
;
if
(
!
set_nonblock
(
fd
,
true
))
goto
close
;
if
(
connect
(
fd
,
addr
->
ai_addr
,
addr
->
ai_addrlen
)
==
0
)
{
/* Immediate connect. */
*
immediate
=
true
;
return
fd
;
}
if
(
errno
==
EINPROGRESS
)
return
fd
;
close:
close_noerr
(
fd
);
return
-
1
;
}
int
net_connect
(
const
struct
addrinfo
*
addrinfo
)
int
net_connect_async
(
const
struct
addrinfo
*
addrinfo
,
struct
pollfd
pfds
[
2
])
{
int
sockfd
=
-
1
,
saved_errno
;
unsigned
int
i
,
num
;
const
struct
addrinfo
*
ipv4
=
NULL
,
*
ipv6
=
NULL
;
const
struct
addrinfo
*
addr
[
MAX_PROTOS
];
socklen_t
slen
[
MAX_PROTOS
];
struct
pollfd
pfd
[
MAX_PROTOS
];
const
struct
addrinfo
*
addr
[
2
]
=
{
NULL
,
NULL
};
unsigned
int
i
;
pfds
[
0
].
fd
=
pfds
[
1
].
fd
=
-
1
;
pfds
[
0
].
events
=
pfds
[
1
].
events
=
POLLOUT
;
/* Give IPv6 a slight advantage, by trying it first. */
for
(;
addrinfo
;
addrinfo
=
addrinfo
->
ai_next
)
{
switch
(
addrinfo
->
ai_family
)
{
case
AF_INET
:
if
(
!
ipv4
)
ipv4
=
addrinfo
;
addr
[
1
]
=
addrinfo
;
break
;
case
AF_INET6
:
if
(
!
ipv6
)
ipv6
=
addrinfo
;
addr
[
0
]
=
addrinfo
;
break
;
default:
continue
;
}
}
num
=
0
;
/* We give IPv6 a slight edge by connecting it first. */
if
(
ipv6
)
{
addr
[
num
]
=
ipv6
;
slen
[
num
]
=
sizeof
(
struct
sockaddr_in6
);
pfd
[
num
].
fd
=
socket
(
AF_INET6
,
ipv6
->
ai_socktype
,
ipv6
->
ai_protocol
);
if
(
pfd
[
num
].
fd
!=
-
1
)
num
++
;
/* In case we found nothing. */
errno
=
ENOENT
;
for
(
i
=
0
;
i
<
2
;
i
++
)
{
bool
immediate
;
if
(
!
addr
[
i
])
continue
;
pfds
[
i
].
fd
=
start_connect
(
addr
[
i
],
&
immediate
);
if
(
immediate
)
{
if
(
pfds
[
!
i
].
fd
!=
-
1
)
close
(
pfds
[
!
i
].
fd
);
if
(
!
set_nonblock
(
pfds
[
i
].
fd
,
false
))
{
close_noerr
(
pfds
[
i
].
fd
);
return
-
1
;
}
return
pfds
[
0
].
fd
;
}
}
if
(
ipv4
)
{
addr
[
num
]
=
ipv4
;
slen
[
num
]
=
sizeof
(
struct
sockaddr_in
);
pfd
[
num
].
fd
=
socket
(
AF_INET
,
ipv4
->
ai_socktype
,
ipv4
->
ai_protocol
);
if
(
pfd
[
num
].
fd
!=
-
1
)
num
++
;
if
(
pfds
[
0
].
fd
!=
-
1
||
pfds
[
1
].
fd
!=
-
1
)
errno
=
EINPROGRESS
;
return
-
1
;
}
void
net_connect_abort
(
struct
pollfd
pfds
[
2
])
{
unsigned
int
i
;
for
(
i
=
0
;
i
<
2
;
i
++
)
{
if
(
pfds
[
i
].
fd
!=
-
1
)
close_noerr
(
pfds
[
i
].
fd
);
pfds
[
i
].
fd
=
-
1
;
}
}
int
net_connect_complete
(
struct
pollfd
pfds
[
2
])
{
unsigned
int
i
;
assert
(
pfds
[
0
].
fd
!=
-
1
||
pfds
[
1
].
fd
!=
-
1
);
for
(
i
=
0
;
i
<
2
;
i
++
)
{
int
err
;
socklen_t
errlen
=
sizeof
(
err
);
for
(
i
=
0
;
i
<
num
;
i
++
)
{
if
(
!
set_nonblock
(
pfd
[
i
].
fd
,
true
))
{
remove_fd
(
pfd
,
addr
,
slen
,
&
num
,
i
--
);
if
(
pfds
[
i
].
fd
==
-
1
)
continue
;
if
(
getsockopt
(
pfds
[
i
].
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
err
,
&
errlen
)
!=
0
)
{
net_connect_abort
(
pfds
);
return
-
1
;
}
/* Connect *can* be instant. */
if
(
connect
(
pfd
[
i
].
fd
,
addr
[
i
]
->
ai_addr
,
slen
[
i
])
==
0
)
goto
got_one
;
if
(
errno
!=
EINPROGRESS
)
{
/* Remove dead one. */
remove_fd
(
pfd
,
addr
,
slen
,
&
num
,
i
--
);
if
(
err
==
0
)
{
/* Don't hand them non-blocking fd! */
if
(
!
set_nonblock
(
pfds
[
i
].
fd
,
false
))
{
net_connect_abort
(
pfds
);
return
-
1
;
}
/* Close other one. */
if
(
pfds
[
!
i
].
fd
!=
-
1
)
close
(
pfds
[
!
i
].
fd
);
return
pfds
[
i
].
fd
;
}
pfd
[
i
].
events
=
POLLOUT
;
}
while
(
num
&&
poll
(
pfd
,
num
,
-
1
)
!=
-
1
)
{
for
(
i
=
0
;
i
<
num
;
i
++
)
{
int
err
;
socklen_t
errlen
=
sizeof
(
err
);
if
(
!
pfd
[
i
].
revents
)
continue
;
if
(
getsockopt
(
pfd
[
i
].
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
err
,
&
errlen
)
!=
0
)
goto
out
;
if
(
err
==
0
)
goto
got_one
;
/* Remove dead one. */
errno
=
err
;
remove_fd
(
pfd
,
addr
,
slen
,
&
num
,
i
--
);
}
}
/* Still going... */
errno
=
EINPROGRESS
;
return
-
1
;
}
got_one:
/* We don't want to hand them a non-blocking socket! */
if
(
set_nonblock
(
pfd
[
i
].
fd
,
false
))
sockfd
=
pfd
[
i
].
fd
;
int
net_connect
(
const
struct
addrinfo
*
addrinfo
)
{
struct
pollfd
pfds
[
2
];
int
sockfd
;
sockfd
=
net_connect_async
(
addrinfo
,
pfds
);
/* Immediate connect or error is easy. */
if
(
sockfd
>=
0
||
errno
!=
EINPROGRESS
)
return
sockfd
;
while
(
poll
(
pfds
,
2
,
-
1
)
!=
-
1
)
{
sockfd
=
net_connect_complete
(
pfds
);
if
(
sockfd
>=
0
||
errno
!=
EINPROGRESS
)
return
sockfd
;
}
out:
saved_errno
=
errno
;
for
(
i
=
0
;
i
<
num
;
i
++
)
if
(
pfd
[
i
].
fd
!=
sockfd
)
close
(
pfd
[
i
].
fd
);
errno
=
saved_errno
;
return
sockfd
;
net_connect_abort
(
pfds
);
return
-
1
;
}
struct
addrinfo
*
net_server_lookup
(
const
char
*
service
,
...
...
ccan/net/net.h
View file @
158de63f
/* Licensed under BSD-MIT - see LICENSE file for details */
#ifndef CCAN_NET_H
#define CCAN_NET_H
#include <stdbool.h>
struct
pollfd
;
/**
* net_client_lookup - look up a network name to connect to.
* @hostname: the name to look up
...
...
@@ -16,6 +20,7 @@
* #include <sys/socket.h>
* #include <stdio.h>
* #include <netdb.h>
* #include <poll.h>
* #include <err.h>
* ...
* struct addrinfo *addr;
...
...
@@ -48,6 +53,67 @@ struct addrinfo *net_client_lookup(const char *hostname,
*/
int
net_connect
(
const
struct
addrinfo
*
addrinfo
);
/**
* net_connect_async - initiate connect to a server
* @addrinfo: linked list struct addrinfo (usually from net_client_lookup).
* @pfds: array of two struct pollfd.
*
* This begins connecting to a server described by @addrinfo,
* and places the one or two file descriptors into pfds[0] and pfds[1].
* It returns a valid file descriptor if connect() returned immediately.
*
* Otherwise it returns -1 and sets errno, most likely EINPROGRESS.
* In this case, poll() on pfds and call net_connect_complete().
*
* Example:
* struct pollfd pfds[2];
* ...
* fd = net_connect_async(addr, pfds);
* if (fd < 0 && errno != EINPROGRESS)
* err(1, "Failed to connect to ccan.ozlabs.org");
*/
int
net_connect_async
(
const
struct
addrinfo
*
addrinfo
,
struct
pollfd
*
pfds
);
/**
* net_connect_complete - complete net_connect_async call.
* @pfds: array of two struct pollfd handed to net_connect_async.
*
* When poll() reports some activity, this determines whether a connection
* has completed. If so, it cleans up and returns the connected fd.
* Otherwise, it returns -1, and sets errno (usually EINPROGRESS).
*
* Example:
* // After net_connect_async.
* while (fd < 0 && errno == EINPROGRESS) {
* // Wait for activity...
* poll(pfds, 2, -1);
* fd = net_connect_complete(pfds);
* }
* if (fd < 0)
* err(1, "connecting");
* printf("Connected on fd %i!\n", fd);
*/
int
net_connect_complete
(
struct
pollfd
*
pfds
);
/**
* net_connect_abort - abort a net_connect_async call.
* @pfds: array of two struct pollfd handed to net_connect_async.
*
* Closes the file descriptors.
*
* Example:
* // After net_connect_async.
* if (poll(pfds, 2, 1000) == 0) { // Timeout.
* net_connect_abort(pfds);
* fd = -1;
* } else {
* fd = net_connect_complete(pfds);
* if (fd < 0)
* err(1, "connecting");
* }
*/
void
net_connect_abort
(
struct
pollfd
*
pfds
);
/**
* net_server_lookup - look up a service name to bind to.
* @service: the service to look up
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment