Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
varnish-cache
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
varnishcache
varnish-cache
Commits
d7a81fe8
Commit
d7a81fe8
authored
May 06, 2019
by
Poul-Henning Kamp
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Introduce a VSS_ResolveOne() function and use it the places where
we want a single unique suckaddr.
parent
069341b8
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
107 additions
and
147 deletions
+107
-147
cache_proxy_proto.c
bin/varnishd/proxy/cache_proxy_proto.c
+13
-37
o00000.vtc
bin/varnishtest/tests/o00000.vtc
+27
-25
vtc_client.c
bin/varnishtest/vtc_client.c
+7
-18
vtc_main.c
bin/varnishtest/vtc_main.c
+5
-9
vtc_misc.c
bin/varnishtest/vtc_misc.c
+8
-19
vss.h
include/vss.h
+3
-0
vss.c
lib/libvarnish/vss.c
+39
-0
vmod_debug_dyn.c
lib/libvmod_debug/vmod_debug_dyn.c
+2
-10
vmod_std_conversions.c
lib/libvmod_std/vmod_std_conversions.c
+3
-29
No files found.
bin/varnishd/proxy/cache_proxy_proto.c
View file @
d7a81fe8
...
...
@@ -40,6 +40,7 @@
#include "vend.h"
#include "vsa.h"
#include "vss.h"
#include "vtcp.h"
struct
vpx_tlv
{
...
...
@@ -61,7 +62,6 @@ vpx_proto1(const struct worker *wrk, const struct req *req)
const
char
*
fld
[
5
];
int
i
;
char
*
p
,
*
q
;
struct
addrinfo
hints
,
*
res
;
struct
suckaddr
*
sa
;
int
pfam
=
-
1
;
...
...
@@ -100,58 +100,34 @@ vpx_proto1(const struct worker *wrk, const struct req *req)
}
if
(
!
strcmp
(
fld
[
0
],
"TCP4"
))
pfam
=
A
F_INET
;
pfam
=
P
F_INET
;
else
if
(
!
strcmp
(
fld
[
0
],
"TCP6"
))
pfam
=
A
F_INET6
;
pfam
=
P
F_INET6
;
else
{
VSL
(
SLT_ProxyGarbage
,
req
->
sp
->
vxid
,
"PROXY1: Wrong TCP[46] field"
);
return
(
-
1
);
}
memset
(
&
hints
,
0
,
sizeof
hints
);
hints
.
ai_socktype
=
SOCK_STREAM
;
hints
.
ai_flags
=
AI_NUMERICHOST
|
AI_NUMERICSERV
;
i
=
getaddrinfo
(
fld
[
1
],
fld
[
3
],
&
hints
,
&
res
);
if
(
i
!=
0
)
{
VSL
(
SLT_ProxyGarbage
,
req
->
sp
->
vxid
,
"PROXY1: Cannot resolve source address (%s)"
,
gai_strerror
(
i
));
return
(
-
1
);
}
AZ
(
res
->
ai_next
);
if
(
res
->
ai_family
!=
pfam
)
{
SES_Reserve_client_addr
(
req
->
sp
,
&
sa
);
if
(
VSS_ResolveOne
(
sa
,
fld
[
1
],
fld
[
3
],
pfam
,
SOCK_STREAM
,
AI_NUMERICHOST
|
AI_NUMERICSERV
)
==
NULL
)
{
VSL
(
SLT_ProxyGarbage
,
req
->
sp
->
vxid
,
"PROXY1: %s got wrong protocol (%d)"
,
fld
[
0
],
res
->
ai_family
);
freeaddrinfo
(
res
);
"PROXY1: Cannot resolve source address"
);
return
(
-
1
);
}
SES_Reserve_client_addr
(
req
->
sp
,
&
sa
);
AN
(
VSA_Build
(
sa
,
res
->
ai_addr
,
res
->
ai_addrlen
));
SES_Set_String_Attr
(
req
->
sp
,
SA_CLIENT_IP
,
fld
[
1
]);
SES_Set_String_Attr
(
req
->
sp
,
SA_CLIENT_PORT
,
fld
[
3
]);
freeaddrinfo
(
res
);
i
=
getaddrinfo
(
fld
[
2
],
fld
[
4
],
&
hints
,
&
res
);
if
(
i
!=
0
)
{
VSL
(
SLT_ProxyGarbage
,
req
->
sp
->
vxid
,
"PROXY1: Cannot resolve destination address (%s)"
,
gai_strerror
(
i
));
return
(
-
1
);
}
AZ
(
res
->
ai_next
);
if
(
res
->
ai_family
!=
pfam
)
{
SES_Reserve_server_addr
(
req
->
sp
,
&
sa
);
if
(
VSS_ResolveOne
(
sa
,
fld
[
2
],
fld
[
4
],
pfam
,
SOCK_STREAM
,
AI_NUMERICHOST
|
AI_NUMERICSERV
)
==
NULL
)
{
VSL
(
SLT_ProxyGarbage
,
req
->
sp
->
vxid
,
"PROXY1: %s got wrong protocol (%d)"
,
fld
[
0
],
res
->
ai_family
);
freeaddrinfo
(
res
);
"PROXY1: Cannot resolve destination address"
);
return
(
-
1
);
}
SES_Reserve_server_addr
(
req
->
sp
,
&
sa
);
AN
(
VSA_Build
(
sa
,
res
->
ai_addr
,
res
->
ai_addrlen
));
freeaddrinfo
(
res
);
SES_Set_String_Attr
(
req
->
sp
,
SA_CLIENT_IP
,
fld
[
1
]);
SES_Set_String_Attr
(
req
->
sp
,
SA_CLIENT_PORT
,
fld
[
3
]);
VSL
(
SLT_Proxy
,
req
->
sp
->
vxid
,
"1 %s %s %s %s"
,
fld
[
1
],
fld
[
3
],
fld
[
2
],
fld
[
4
]);
...
...
bin/varnishtest/tests/o00000.vtc
View file @
d7a81fe8
...
...
@@ -34,10 +34,10 @@ logexpect l1 -v v1 {
expect * 1002 ProxyGarbage "PROXY1: Too few fields"
expect * 1003 ProxyGarbage "PROXY1: Too many fields"
expect * 1004 ProxyGarbage "PROXY1: Wrong TCP\\[46\\] field"
expect * 1005 ProxyGarbage "PROXY1: Cannot resolve source address
\\(.*\\)
"
expect * 1007 ProxyGarbage "PROXY1: Cannot resolve destination address
\\(.*\\)
"
expect * 10
13 ProxyGarbage "PROXY1: TCP4 got wrong protocol \\([0-9]*\\)
"
expect * 101
4 ProxyGarbage "PROXY1: TCP6 got wrong protocol \\([0-9]*\\)
"
expect * 1005 ProxyGarbage "PROXY1: Cannot resolve source address"
expect * 1007 ProxyGarbage "PROXY1: Cannot resolve destination address"
expect * 10
09 ProxyGarbage "PROXY1: Cannot resolve source address
"
expect * 101
1 ProxyGarbage "PROXY1: Cannot resolve source address
"
expect * 1015 Proxy "1 1.2.3.4 1234 5.6.7.8 5678"
expect * 1018 Proxy "1 1:f::2 1234 5:a::8 5678"
expect * 1021 Proxy "1 1:f::3 1234 5:a::8 5678"
...
...
@@ -50,7 +50,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY "
...
...
@@ -58,7 +58,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY A B C D\r\n"
...
...
@@ -66,7 +66,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY A B C D E F\r\n"
...
...
@@ -74,7 +74,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY A B C D E\r\n"
...
...
@@ -82,7 +82,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP4 B C D E\r\n"
...
...
@@ -90,7 +90,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP4 1.2.3.4 C D E\r\n"
...
...
@@ -98,7 +98,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP4 1.2.3.4 D 1234 E\r\n"
...
...
@@ -106,7 +106,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP4 1.2.3.4 5.6.7.8 1234 E\r\n"
...
...
@@ -114,7 +114,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP6 B C D E\r\n"
...
...
@@ -122,7 +122,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP6 1:f::2 C D E\r\n"
...
...
@@ -130,7 +130,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP6 1:f::2 1234 D E\r\n"
...
...
@@ -138,7 +138,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP6 1:f::2 5:a::8 1234 E\r\n"
...
...
@@ -146,7 +146,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP4 1:f::2 5:a::8 1234 5678\r\n"
...
...
@@ -154,7 +154,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
client c1 {
send "PROXY TCP6 1.2.3.4 5.6.7.8 1234 5678\r\n"
...
...
@@ -162,7 +162,7 @@ client c1 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
# Finally try something which works...
client c1 -proxy1 "1.2.3.4:1234 5.6.7.8:5678" {
...
...
@@ -179,7 +179,7 @@ client c1 -proxy1 "1.2.3.4:1234 5.6.7.8:5678" {
expect resp.http.rp != "1234"
} -run
delay .1
varnish v1 -vsl_catchup
client c1 -proxy1 "[1:f::2]:1234 [5:a::8]:5678" {
txreq -url /2
...
...
@@ -195,7 +195,7 @@ client c1 -proxy1 "[1:f::2]:1234 [5:a::8]:5678" {
expect resp.http.rp != "1234"
} -run
delay .1
varnish v1 -vsl_catchup
# Try with appended request (See also: #1728)
client c2 {
...
...
@@ -204,13 +204,15 @@ client c2 {
expect resp.http.url == "/3"
} -run
varnish v1 -vsl_catchup
# Malformed (missing \r)
client c2 {
send "PROXY TCP4 1.2.3.4 5.6.7.8 1234 5678\n"
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
# Malformed, too long (106)
# NB: Should check VSL for proper disposal
...
...
@@ -219,7 +221,7 @@ client c2 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
# Malformed, too long (107)
# NB: Should check VSL for proper disposal
...
...
@@ -228,7 +230,7 @@ client c2 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
# Malformed, too long (108)
# NB: Should check VSL for proper disposal
...
...
@@ -237,6 +239,6 @@ client c2 {
expect_close
} -run
delay .1
varnish v1 -vsl_catchup
logexpect l1 -wait
bin/varnishtest/vtc_client.c
View file @
d7a81fe8
...
...
@@ -31,6 +31,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
...
...
@@ -73,21 +74,11 @@ static VTAILQ_HEAD(, client) clients =
* Send the proxy header
*/
static
int
v_matchproto_
(
vss_resolved_f
)
proxy_cb
(
void
*
priv
,
const
struct
suckaddr
*
sa
)
{
struct
suckaddr
**
addr
=
priv
;
*
addr
=
VSA_Clone
(
sa
);
return
(
1
);
}
static
void
client_proxy
(
struct
vtclog
*
vl
,
int
fd
,
int
version
,
const
char
*
spec
)
{
struct
suckaddr
*
sac
,
*
sas
;
const
char
*
err
;
char
*
p
,
*
p2
;
int
error
;
p
=
strdup
(
spec
);
AN
(
p
);
...
...
@@ -95,14 +86,12 @@ client_proxy(struct vtclog *vl, int fd, int version, const char *spec)
AN
(
p2
);
*
p2
++
=
'\0'
;
error
=
VSS_resolver
(
p
,
NULL
,
proxy_cb
,
&
sac
,
&
err
);
if
(
err
!=
NULL
)
vtc_fatal
(
vl
,
"Could not resolve client address: %s"
,
err
);
assert
(
error
==
1
);
error
=
VSS_resolver
(
p2
,
NULL
,
proxy_cb
,
&
sas
,
&
err
);
if
(
err
!=
NULL
)
vtc_fatal
(
vl
,
"Could not resolve server address: %s"
,
err
);
assert
(
error
==
1
);
sac
=
VSS_ResolveOne
(
NULL
,
p
,
NULL
,
0
,
SOCK_STREAM
,
AI_PASSIVE
);
if
(
sac
==
NULL
)
vtc_fatal
(
vl
,
"Could not resolve client address"
);
sas
=
VSS_ResolveOne
(
NULL
,
p2
,
NULL
,
0
,
SOCK_STREAM
,
AI_PASSIVE
);
if
(
sas
==
NULL
)
vtc_fatal
(
vl
,
"Could not resolve server address"
);
if
(
vtc_send_proxy
(
fd
,
version
,
sac
,
sas
))
vtc_fatal
(
vl
,
"Write failed: %s"
,
strerror
(
errno
));
free
(
p
);
...
...
bin/varnishtest/vtc_main.c
View file @
d7a81fe8
...
...
@@ -38,6 +38,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include "vtc.h"
...
...
@@ -550,18 +551,11 @@ i_mode(void)
* Figure out what IP related magic
*/
static
int
v_matchproto_
(
vss_resolved_f
)
bind_cb
(
void
*
priv
,
const
struct
suckaddr
*
sa
)
{
(
void
)
priv
;
return
(
VTCP_bind
(
sa
,
NULL
));
}
static
void
ip_magic
(
void
)
{
const
char
*
p
;
int
fd
;
struct
suckaddr
*
sa
;
char
abuf
[
VTCP_ADDRBUFSIZE
];
char
pbuf
[
VTCP_PORTBUFSIZE
];
...
...
@@ -572,7 +566,9 @@ ip_magic(void)
* XXX: "localhost", but that doesn't work out of the box.
* XXX: Things like "prefer_ipv6" parameter complicates things.
*/
fd
=
VSS_resolver
(
"127.0.0.1"
,
NULL
,
bind_cb
,
NULL
,
&
p
);
sa
=
VSS_ResolveOne
(
NULL
,
"127.0.0.1"
,
"0"
,
0
,
SOCK_STREAM
,
0
);
AN
(
sa
);
fd
=
VTCP_bind
(
sa
,
NULL
);
assert
(
fd
>=
0
);
VTCP_myname
(
fd
,
abuf
,
sizeof
abuf
,
pbuf
,
sizeof
(
pbuf
));
extmacro_def
(
"localhost"
,
"%s"
,
abuf
);
...
...
bin/varnishtest/vtc_misc.c
View file @
d7a81fe8
...
...
@@ -344,30 +344,19 @@ cmd_delay(CMD_ARGS)
* DNS services. This is a basic sanity check for those.
*/
static
int
v_matchproto_
(
vss_resolved_f
)
dns_
cb
(
void
*
priv
,
const
struct
suckaddr
*
sa
)
static
int
dns_
works
(
void
)
{
struct
suckaddr
*
sa
;
char
abuf
[
VTCP_ADDRBUFSIZE
];
char
pbuf
[
VTCP_PORTBUFSIZE
];
int
*
ret
=
priv
;
sa
=
VSS_ResolveOne
(
NULL
,
"dns-canary.freebsd.dk"
,
NULL
,
0
,
0
,
0
);
if
(
sa
==
NULL
)
return
(
0
);
VTCP_name
(
sa
,
abuf
,
sizeof
abuf
,
pbuf
,
sizeof
pbuf
);
if
(
strcmp
(
abuf
,
"192.0.2.255"
))
{
fprintf
(
stderr
,
"DNS-test: Wrong response: %s
\n
"
,
abuf
);
*
ret
=
-
1
;
}
else
if
(
*
ret
==
0
)
*
ret
=
1
;
return
(
0
);
}
static
int
dns_works
(
void
)
{
int
ret
=
0
,
error
;
const
char
*
msg
;
error
=
VSS_resolver
(
"dns-canary.freebsd.dk"
,
NULL
,
dns_cb
,
&
ret
,
&
msg
);
if
(
error
||
msg
!=
NULL
||
ret
!=
1
)
free
(
sa
);
if
(
strcmp
(
abuf
,
"192.0.2.255"
))
return
(
0
);
return
(
1
);
}
...
...
include/vss.h
View file @
d7a81fe8
...
...
@@ -34,3 +34,6 @@ int VSS_resolver(const char *addr, const char *def_port, vss_resolved_f *func,
void
*
priv
,
const
char
**
err
);
int
VSS_resolver_socktype
(
const
char
*
addr
,
const
char
*
def_port
,
vss_resolved_f
*
func
,
void
*
priv
,
const
char
**
err
,
int
socktype
);
struct
suckaddr
*
VSS_ResolveOne
(
void
*
dst
,
const
char
*
addr
,
const
char
*
port
,
int
family
,
int
socktype
,
int
flags
);
lib/libvarnish/vss.c
View file @
d7a81fe8
...
...
@@ -154,3 +154,42 @@ VSS_resolver(const char *addr, const char *def_port, vss_resolved_f *func,
return
(
VSS_resolver_socktype
(
addr
,
def_port
,
func
,
priv
,
err
,
SOCK_STREAM
));
}
#include <stdio.h>
struct
suckaddr
*
VSS_ResolveOne
(
void
*
dst
,
const
char
*
addr
,
const
char
*
port
,
int
family
,
int
socktype
,
int
flags
)
{
struct
addrinfo
hints
,
*
res
=
NULL
;
struct
suckaddr
*
retval
=
NULL
;
char
*
p
=
NULL
,
*
hp
,
*
pp
;
int
error
;
memset
(
&
hints
,
0
,
sizeof
hints
);
hints
.
ai_family
=
family
;
hints
.
ai_socktype
=
socktype
;
hints
.
ai_flags
=
flags
;
AN
(
addr
);
if
(
port
!=
NULL
)
{
error
=
getaddrinfo
(
addr
,
port
,
&
hints
,
&
res
);
}
else
{
p
=
strdup
(
addr
);
AN
(
p
);
if
(
vss_parse
(
p
,
&
hp
,
&
pp
)
!=
NULL
||
pp
==
NULL
)
{
free
(
p
);
return
(
NULL
);
}
error
=
getaddrinfo
(
hp
,
pp
,
&
hints
,
&
res
);
free
(
p
);
}
if
(
!
error
&&
res
!=
NULL
&&
res
->
ai_next
==
NULL
)
{
if
(
dst
==
NULL
)
retval
=
VSA_Malloc
(
res
->
ai_addr
,
res
->
ai_addrlen
);
else
retval
=
VSA_Build
(
dst
,
res
->
ai_addr
,
res
->
ai_addrlen
);
freeaddrinfo
(
res
);
}
return
(
retval
);
}
lib/libvmod_debug/vmod_debug_dyn.c
View file @
d7a81fe8
...
...
@@ -38,6 +38,7 @@
#include "cache/cache.h"
#include "vsa.h"
#include "vss.h"
#include "vcc_if.h"
struct
xyzzy_debug_dyn
{
...
...
@@ -60,7 +61,6 @@ static void
dyn_dir_init
(
VRT_CTX
,
struct
xyzzy_debug_dyn
*
dyn
,
VCL_STRING
addr
,
VCL_STRING
port
,
VCL_PROBE
probe
)
{
struct
addrinfo
hints
,
*
res
=
NULL
;
struct
suckaddr
*
sa
;
VCL_BACKEND
dir
,
dir2
;
struct
vrt_backend
vrt
;
...
...
@@ -75,13 +75,7 @@ dyn_dir_init(VRT_CTX, struct xyzzy_debug_dyn *dyn,
vrt
.
hosthdr
=
addr
;
vrt
.
probe
=
probe
;
memset
(
&
hints
,
0
,
sizeof
(
hints
));
hints
.
ai_family
=
AF_UNSPEC
;
hints
.
ai_socktype
=
SOCK_STREAM
;
AZ
(
getaddrinfo
(
addr
,
port
,
&
hints
,
&
res
));
XXXAZ
(
res
->
ai_next
);
sa
=
VSA_Malloc
(
res
->
ai_addr
,
res
->
ai_addrlen
);
sa
=
VSS_ResolveOne
(
NULL
,
addr
,
port
,
AF_UNSPEC
,
SOCK_STREAM
,
0
);
AN
(
sa
);
if
(
VSA_Get_Proto
(
sa
)
==
AF_INET
)
{
vrt
.
ipv4_addr
=
addr
;
...
...
@@ -92,8 +86,6 @@ dyn_dir_init(VRT_CTX, struct xyzzy_debug_dyn *dyn,
}
else
WRONG
(
"Wrong proto family"
);
freeaddrinfo
(
res
);
dir
=
VRT_new_backend
(
ctx
,
&
vrt
);
AN
(
dir
);
...
...
lib/libvmod_std/vmod_std_conversions.c
View file @
d7a81fe8
...
...
@@ -40,6 +40,7 @@
#include "vnum.h"
#include "vsa.h"
#include "vss.h"
#include "vtim.h"
#include "vcc_if.h"
...
...
@@ -189,34 +190,6 @@ vmod_integer(VRT_CTX, struct VARGS(integer) *a)
return
(
0
);
}
static
VCL_IP
lookup
(
VCL_IP
*
p
,
const
char
*
s
,
int
resolve
)
{
struct
addrinfo
hints
,
*
res0
=
NULL
;
const
struct
addrinfo
*
res
;
int
error
;
VCL_IP
retval
=
NULL
;
if
(
s
==
NULL
)
return
(
retval
);
memset
(
&
hints
,
0
,
sizeof
(
hints
));
hints
.
ai_family
=
PF_UNSPEC
;
hints
.
ai_socktype
=
SOCK_STREAM
;
if
(
!
resolve
)
hints
.
ai_flags
|=
AI_NUMERICHOST
;
error
=
getaddrinfo
(
s
,
"80"
,
&
hints
,
&
res0
);
if
(
!
error
)
{
for
(
res
=
res0
;
res
!=
NULL
;
res
=
res
->
ai_next
)
{
retval
=
VSA_Build
(
p
,
res
->
ai_addr
,
res
->
ai_addrlen
);
if
(
retval
!=
NULL
)
break
;
}
freeaddrinfo
(
res0
);
}
return
(
retval
);
}
VCL_IP
vmod_ip
(
VRT_CTX
,
struct
VARGS
(
ip
)
*
a
)
{
...
...
@@ -234,7 +207,8 @@ vmod_ip(VRT_CTX, struct VARGS(ip) *a)
return
(
NULL
);
}
retval
=
lookup
(
p
,
a
->
s
,
a
->
resolve
);
retval
=
VSS_ResolveOne
(
p
,
a
->
s
,
"80"
,
PF_UNSPEC
,
SOCK_STREAM
,
a
->
resolve
?
0
:
AI_NUMERICHOST
);
if
(
retval
!=
NULL
)
return
(
retval
);
...
...
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