| OLD | NEW |
| 1 #include "debug.h" | 1 #include "debug.h" |
| 2 #include "sandbox_impl.h" | 2 #include "sandbox_impl.h" |
| 3 | 3 |
| 4 namespace playground { | 4 namespace playground { |
| 5 | 5 |
| 6 #if defined(__NR_socket) | 6 #if defined(__NR_socket) |
| 7 | 7 |
| 8 ssize_t Sandbox::sandbox_recvfrom(int sockfd, void* buf, size_t len, int flags, | 8 ssize_t Sandbox::sandbox_recvfrom(int sockfd, void* buf, size_t len, int flags, |
| 9 void* from, socklen_t* fromlen) { | 9 void* from, socklen_t* fromlen) { |
| 10 Debug::syscall(__NR_recvfrom, "Executing handler"); | 10 Debug::syscall(__NR_recvfrom, "Executing handler"); |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 long rc; | 197 long rc; |
| 198 SysCalls sys; | 198 SysCalls sys; |
| 199 if (write(sys, processFdPub(), &request, sizeof(request)) != | 199 if (write(sys, processFdPub(), &request, sizeof(request)) != |
| 200 sizeof(request) || | 200 sizeof(request) || |
| 201 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | 201 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
| 202 die("Failed to forward getsockopt() request [sandbox]"); | 202 die("Failed to forward getsockopt() request [sandbox]"); |
| 203 } | 203 } |
| 204 return static_cast<int>(rc); | 204 return static_cast<int>(rc); |
| 205 } | 205 } |
| 206 | 206 |
| 207 bool Sandbox::process_recvfrom(int parentProc, int sandboxFd, int threadFdPub, | 207 bool Sandbox::process_recvfrom(int parentMapsFd, int sandboxFd, |
| 208 int threadFd, SecureMem::Args* mem) { | 208 int threadFdPub, int threadFd, |
| 209 SecureMem::Args* mem) { |
| 209 // Read request | 210 // Read request |
| 210 RecvFrom recvfrom_req; | 211 RecvFrom recvfrom_req; |
| 211 SysCalls sys; | 212 SysCalls sys; |
| 212 if (read(sys, sandboxFd, &recvfrom_req, sizeof(recvfrom_req)) != | 213 if (read(sys, sandboxFd, &recvfrom_req, sizeof(recvfrom_req)) != |
| 213 sizeof(recvfrom_req)) { | 214 sizeof(recvfrom_req)) { |
| 214 die("Failed to read parameters for recvfrom() [process]"); | 215 die("Failed to read parameters for recvfrom() [process]"); |
| 215 } | 216 } |
| 216 | 217 |
| 217 // Unsupported flag encountered. Deny the call. | 218 // Unsupported flag encountered. Deny the call. |
| 218 if (recvfrom_req.flags & | 219 if (recvfrom_req.flags & |
| 219 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | 220 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
| 220 SecureMem::abandonSystemCall(threadFd, -EINVAL); | 221 SecureMem::abandonSystemCall(threadFd, -EINVAL); |
| 221 return false; | 222 return false; |
| 222 } | 223 } |
| 223 | 224 |
| 224 // While we do not anticipate any particular need to receive data on | 225 // While we do not anticipate any particular need to receive data on |
| 225 // unconnected sockets, there is no particular risk in doing so. | 226 // unconnected sockets, there is no particular risk in doing so. |
| 226 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | 227 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
| 227 __NR_recvfrom, recvfrom_req.sockfd, | 228 __NR_recvfrom, recvfrom_req.sockfd, |
| 228 recvfrom_req.buf, recvfrom_req.len, | 229 recvfrom_req.buf, recvfrom_req.len, |
| 229 recvfrom_req.flags, recvfrom_req.from, | 230 recvfrom_req.flags, recvfrom_req.from, |
| 230 recvfrom_req.fromlen); | 231 recvfrom_req.fromlen); |
| 231 return true; | 232 return true; |
| 232 } | 233 } |
| 233 | 234 |
| 234 bool Sandbox::process_recvmsg(int parentProc, int sandboxFd, int threadFdPub, | 235 bool Sandbox::process_recvmsg(int parentMapsFd, int sandboxFd, int threadFdPub, |
| 235 int threadFd, SecureMem::Args* mem) { | 236 int threadFd, SecureMem::Args* mem) { |
| 236 // Read request | 237 // Read request |
| 237 RecvMsg recvmsg_req; | 238 RecvMsg recvmsg_req; |
| 238 SysCalls sys; | 239 SysCalls sys; |
| 239 if (read(sys, sandboxFd, &recvmsg_req, sizeof(recvmsg_req)) != | 240 if (read(sys, sandboxFd, &recvmsg_req, sizeof(recvmsg_req)) != |
| 240 sizeof(recvmsg_req)) { | 241 sizeof(recvmsg_req)) { |
| 241 die("Failed to read parameters for recvmsg() [process]"); | 242 die("Failed to read parameters for recvmsg() [process]"); |
| 242 } | 243 } |
| 243 | 244 |
| 244 // Unsupported flag encountered. Deny the call. | 245 // Unsupported flag encountered. Deny the call. |
| 245 if (recvmsg_req.flags & | 246 if (recvmsg_req.flags & |
| 246 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | 247 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
| 247 SecureMem::abandonSystemCall(threadFd, -EINVAL); | 248 SecureMem::abandonSystemCall(threadFd, -EINVAL); |
| 248 return false; | 249 return false; |
| 249 } | 250 } |
| 250 | 251 |
| 251 // Receiving messages is general not security critical. | 252 // Receiving messages is general not security critical. |
| 252 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | 253 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
| 253 __NR_recvmsg, recvmsg_req.sockfd, | 254 __NR_recvmsg, recvmsg_req.sockfd, |
| 254 recvmsg_req.msg, recvmsg_req.flags); | 255 recvmsg_req.msg, recvmsg_req.flags); |
| 255 return true; | 256 return true; |
| 256 } | 257 } |
| 257 | 258 |
| 258 bool Sandbox::process_sendmsg(int parentProc, int sandboxFd, int threadFdPub, | 259 bool Sandbox::process_sendmsg(int parentMapsFd, int sandboxFd, int threadFdPub, |
| 259 int threadFd, SecureMem::Args* mem) { | 260 int threadFd, SecureMem::Args* mem) { |
| 260 // Read request | 261 // Read request |
| 261 struct { | 262 struct { |
| 262 SendMsg sendmsg_req; | 263 SendMsg sendmsg_req; |
| 263 struct msghdr msg; | 264 struct msghdr msg; |
| 264 } __attribute__((packed)) data; | 265 } __attribute__((packed)) data; |
| 265 SysCalls sys; | 266 SysCalls sys; |
| 266 if (read(sys, sandboxFd, &data, sizeof(data)) != sizeof(data)) { | 267 if (read(sys, sandboxFd, &data, sizeof(data)) != sizeof(data)) { |
| 267 die("Failed to read parameters for sendmsg() [process]"); | 268 die("Failed to read parameters for sendmsg() [process]"); |
| 268 } | 269 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 do { | 301 do { |
| 301 if (cmsg->cmsg_level != SOL_SOCKET || | 302 if (cmsg->cmsg_level != SOL_SOCKET || |
| 302 cmsg->cmsg_type != SCM_RIGHTS) { | 303 cmsg->cmsg_type != SCM_RIGHTS) { |
| 303 goto deny; | 304 goto deny; |
| 304 } | 305 } |
| 305 } while ((cmsg = CMSG_NXTHDR(&data.msg, cmsg)) != NULL); | 306 } while ((cmsg = CMSG_NXTHDR(&data.msg, cmsg)) != NULL); |
| 306 } | 307 } |
| 307 | 308 |
| 308 // This must be a locked system call, because we have to ensure that the | 309 // This must be a locked system call, because we have to ensure that the |
| 309 // untrusted code does not tamper with the msghdr after we have examined it. | 310 // untrusted code does not tamper with the msghdr after we have examined it. |
| 310 SecureMem::lockSystemCall(parentProc, mem); | 311 SecureMem::lockSystemCall(parentMapsFd, mem); |
| 311 if (sizeof(extra) > 0) { | 312 if (sizeof(extra) > 0) { |
| 312 if (data.msg.msg_namelen > 0) { | 313 if (data.msg.msg_namelen > 0) { |
| 313 data.msg.msg_name = mem->pathname + sizeof(struct msghdr); | 314 data.msg.msg_name = mem->pathname + sizeof(struct msghdr); |
| 314 } | 315 } |
| 315 if (data.msg.msg_controllen > 0) { | 316 if (data.msg.msg_controllen > 0) { |
| 316 data.msg.msg_control = mem->pathname + sizeof(struct msghdr) + | 317 data.msg.msg_control = mem->pathname + sizeof(struct msghdr) + |
| 317 data.msg.msg_namelen; | 318 data.msg.msg_namelen; |
| 318 } | 319 } |
| 319 memcpy(mem->pathname + sizeof(struct msghdr), extra, sizeof(extra)); | 320 memcpy(mem->pathname + sizeof(struct msghdr), extra, sizeof(extra)); |
| 320 } | 321 } |
| 321 memcpy(mem->pathname, &data.msg, sizeof(struct msghdr)); | 322 memcpy(mem->pathname, &data.msg, sizeof(struct msghdr)); |
| 322 SecureMem::sendSystemCall(threadFdPub, true, parentProc, mem, | 323 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, |
| 323 __NR_sendmsg, data.sendmsg_req.sockfd, | 324 __NR_sendmsg, data.sendmsg_req.sockfd, |
| 324 mem->pathname - (char*)mem + (char*)mem->self, | 325 mem->pathname - (char*)mem + (char*)mem->self, |
| 325 data.sendmsg_req.flags); | 326 data.sendmsg_req.flags); |
| 326 return true; | 327 return true; |
| 327 } | 328 } |
| 328 | 329 |
| 329 bool Sandbox::process_sendto(int parentProc, int sandboxFd, int threadFdPub, | 330 bool Sandbox::process_sendto(int parentMapsFd, int sandboxFd, int threadFdPub, |
| 330 int threadFd, SecureMem::Args* mem) { | 331 int threadFd, SecureMem::Args* mem) { |
| 331 // Read request | 332 // Read request |
| 332 SendTo sendto_req; | 333 SendTo sendto_req; |
| 333 SysCalls sys; | 334 SysCalls sys; |
| 334 if (read(sys, sandboxFd, &sendto_req, sizeof(sendto_req)) != | 335 if (read(sys, sandboxFd, &sendto_req, sizeof(sendto_req)) != |
| 335 sizeof(sendto_req)) { | 336 sizeof(sendto_req)) { |
| 336 die("Failed to read parameters for sendto() [process]"); | 337 die("Failed to read parameters for sendto() [process]"); |
| 337 } | 338 } |
| 338 | 339 |
| 339 // The sandbox does not allow sending to arbitrary addresses. | 340 // The sandbox does not allow sending to arbitrary addresses. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 352 // Sending data on a connected socket is similar to calling write(). | 353 // Sending data on a connected socket is similar to calling write(). |
| 353 // Allow it. | 354 // Allow it. |
| 354 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, | 355 SecureMem::sendSystemCall(threadFdPub, false, -1, mem, |
| 355 __NR_sendto, sendto_req.sockfd, | 356 __NR_sendto, sendto_req.sockfd, |
| 356 sendto_req.buf, sendto_req.len, | 357 sendto_req.buf, sendto_req.len, |
| 357 sendto_req.flags, sendto_req.to, | 358 sendto_req.flags, sendto_req.to, |
| 358 sendto_req.tolen); | 359 sendto_req.tolen); |
| 359 return true; | 360 return true; |
| 360 } | 361 } |
| 361 | 362 |
| 362 bool Sandbox::process_setsockopt(int parentProc, int sandboxFd, | 363 bool Sandbox::process_setsockopt(int parentMapsFd, int sandboxFd, |
| 363 int threadFdPub, int threadFd, | 364 int threadFdPub, int threadFd, |
| 364 SecureMem::Args* mem) { | 365 SecureMem::Args* mem) { |
| 365 // Read request | 366 // Read request |
| 366 SetSockOpt setsockopt_req; | 367 SetSockOpt setsockopt_req; |
| 367 SysCalls sys; | 368 SysCalls sys; |
| 368 if (read(sys, sandboxFd, &setsockopt_req, sizeof(setsockopt_req)) != | 369 if (read(sys, sandboxFd, &setsockopt_req, sizeof(setsockopt_req)) != |
| 369 sizeof(setsockopt_req)) { | 370 sizeof(setsockopt_req)) { |
| 370 die("Failed to read parameters for setsockopt() [process]"); | 371 die("Failed to read parameters for setsockopt() [process]"); |
| 371 } | 372 } |
| 372 | 373 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 416 break; | 417 break; |
| 417 } | 418 } |
| 418 break; | 419 break; |
| 419 default: | 420 default: |
| 420 break; | 421 break; |
| 421 } | 422 } |
| 422 SecureMem::abandonSystemCall(threadFd, -EINVAL); | 423 SecureMem::abandonSystemCall(threadFd, -EINVAL); |
| 423 return false; | 424 return false; |
| 424 } | 425 } |
| 425 | 426 |
| 426 bool Sandbox::process_getsockopt(int parentProc, int sandboxFd, | 427 bool Sandbox::process_getsockopt(int parentMapsFd, int sandboxFd, |
| 427 int threadFdPub, int threadFd, | 428 int threadFdPub, int threadFd, |
| 428 SecureMem::Args* mem) { | 429 SecureMem::Args* mem) { |
| 429 // Read request | 430 // Read request |
| 430 GetSockOpt getsockopt_req; | 431 GetSockOpt getsockopt_req; |
| 431 SysCalls sys; | 432 SysCalls sys; |
| 432 if (read(sys, sandboxFd, &getsockopt_req, sizeof(getsockopt_req)) != | 433 if (read(sys, sandboxFd, &getsockopt_req, sizeof(getsockopt_req)) != |
| 433 sizeof(getsockopt_req)) { | 434 sizeof(getsockopt_req)) { |
| 434 die("Failed to read parameters for getsockopt() [process]"); | 435 die("Failed to read parameters for getsockopt() [process]"); |
| 435 } | 436 } |
| 436 | 437 |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 699 // Send request to trusted process and collect response from trusted thread. | 700 // Send request to trusted process and collect response from trusted thread. |
| 700 long rc; | 701 long rc; |
| 701 ssize_t len = sizeof(struct Request) + numExtraData; | 702 ssize_t len = sizeof(struct Request) + numExtraData; |
| 702 if (write(sys, processFdPub(), data, len) != len || | 703 if (write(sys, processFdPub(), data, len) != len || |
| 703 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { | 704 read(sys, threadFdPub(), &rc, sizeof(rc)) != sizeof(rc)) { |
| 704 die("Failed to forward socketcall() request [sandbox]"); | 705 die("Failed to forward socketcall() request [sandbox]"); |
| 705 } | 706 } |
| 706 return static_cast<int>(rc); | 707 return static_cast<int>(rc); |
| 707 } | 708 } |
| 708 | 709 |
| 709 bool Sandbox::process_socketcall(int parentProc, int sandboxFd, | 710 bool Sandbox::process_socketcall(int parentMapsFd, int sandboxFd, |
| 710 int threadFdPub, int threadFd, | 711 int threadFdPub, int threadFd, |
| 711 SecureMem::Args* mem) { | 712 SecureMem::Args* mem) { |
| 712 // Read request | 713 // Read request |
| 713 SocketCall socketcall_req; | 714 SocketCall socketcall_req; |
| 714 SysCalls sys; | 715 SysCalls sys; |
| 715 if (read(sys, sandboxFd, &socketcall_req, sizeof(socketcall_req)) != | 716 if (read(sys, sandboxFd, &socketcall_req, sizeof(socketcall_req)) != |
| 716 sizeof(socketcall_req)) { | 717 sizeof(socketcall_req)) { |
| 717 die("Failed to read parameters for socketcall() [process]"); | 718 die("Failed to read parameters for socketcall() [process]"); |
| 718 } | 719 } |
| 719 | 720 |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 817 goto deny; | 818 goto deny; |
| 818 } | 819 } |
| 819 // Sending data on a connected socket is similar to calling write(). | 820 // Sending data on a connected socket is similar to calling write(). |
| 820 // Allow it. | 821 // Allow it. |
| 821 | 822 |
| 822 accept_complex: | 823 accept_complex: |
| 823 // The parameter block contains potentially security critical information | 824 // The parameter block contains potentially security critical information |
| 824 // that should not be tampered with after it has been inspected. Copy it | 825 // that should not be tampered with after it has been inspected. Copy it |
| 825 // into the write-protected securely shared memory before telling the | 826 // into the write-protected securely shared memory before telling the |
| 826 // trusted thread to execute the socket call. | 827 // trusted thread to execute the socket call. |
| 827 SecureMem::lockSystemCall(parentProc, mem); | 828 SecureMem::lockSystemCall(parentMapsFd, mem); |
| 828 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); | 829 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); |
| 829 SecureMem::sendSystemCall(threadFdPub, true, parentProc, mem, | 830 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, |
| 830 __NR_socketcall, socketcall_req.call, | 831 __NR_socketcall, socketcall_req.call, |
| 831 mem->pathname - (char*)mem + (char*)mem->self); | 832 mem->pathname - (char*)mem + (char*)mem->self); |
| 832 return true; | 833 return true; |
| 833 case SYS_RECVFROM: | 834 case SYS_RECVFROM: |
| 834 // While we do not anticipate any particular need to receive data on | 835 // While we do not anticipate any particular need to receive data on |
| 835 // unconnected sockets, there is no particular risk in doing so. | 836 // unconnected sockets, there is no particular risk in doing so. |
| 836 // Fall through | 837 // Fall through |
| 837 case SYS_RECV: | 838 case SYS_RECV: |
| 838 if (socketcall_req.args.recv.flags & | 839 if (socketcall_req.args.recv.flags & |
| 839 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | 840 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 963 if (cmsg->cmsg_level != SOL_SOCKET || | 964 if (cmsg->cmsg_level != SOL_SOCKET || |
| 964 cmsg->cmsg_type != SCM_RIGHTS) { | 965 cmsg->cmsg_type != SCM_RIGHTS) { |
| 965 goto deny; | 966 goto deny; |
| 966 } | 967 } |
| 967 } while ((cmsg = CMSG_NXTHDR(msg, cmsg)) != NULL); | 968 } while ((cmsg = CMSG_NXTHDR(msg, cmsg)) != NULL); |
| 968 } | 969 } |
| 969 | 970 |
| 970 // This must be a locked system call, because we have to ensure that | 971 // This must be a locked system call, because we have to ensure that |
| 971 // the untrusted code does not tamper with the msghdr after we have | 972 // the untrusted code does not tamper with the msghdr after we have |
| 972 // examined it. | 973 // examined it. |
| 973 SecureMem::lockSystemCall(parentProc, mem); | 974 SecureMem::lockSystemCall(parentMapsFd, mem); |
| 974 socketcall_req.args.sendmsg.msg = | 975 socketcall_req.args.sendmsg.msg = |
| 975 reinterpret_cast<struct msghdr*>(mem->pathname + | 976 reinterpret_cast<struct msghdr*>(mem->pathname + |
| 976 sizeof(socketcall_req.args) - | 977 sizeof(socketcall_req.args) - |
| 977 (char*)mem + (char*)mem->self); | 978 (char*)mem + (char*)mem->self); |
| 978 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); | 979 memcpy(mem->pathname, &socketcall_req.args, sizeof(socketcall_req.args)); |
| 979 if (numSendmsgExtra) { | 980 if (numSendmsgExtra) { |
| 980 if (msg->msg_namelen > 0) { | 981 if (msg->msg_namelen > 0) { |
| 981 msg->msg_name = const_cast<struct msghdr*>( | 982 msg->msg_name = const_cast<struct msghdr*>( |
| 982 socketcall_req.args.sendmsg.msg) + 1; | 983 socketcall_req.args.sendmsg.msg) + 1; |
| 983 } | 984 } |
| 984 if (msg->msg_controllen > 0) { | 985 if (msg->msg_controllen > 0) { |
| 985 msg->msg_control = (char *)( | 986 msg->msg_control = (char *)( |
| 986 socketcall_req.args.sendmsg.msg + 1) + msg->msg_namelen; | 987 socketcall_req.args.sendmsg.msg + 1) + msg->msg_namelen; |
| 987 } | 988 } |
| 988 memcpy(mem->pathname + sizeof(socketcall_req.args) + sizeof(*msg), | 989 memcpy(mem->pathname + sizeof(socketcall_req.args) + sizeof(*msg), |
| 989 sendmsgExtra, numSendmsgExtra); | 990 sendmsgExtra, numSendmsgExtra); |
| 990 } | 991 } |
| 991 memcpy(mem->pathname + sizeof(socketcall_req.args), msg, sizeof(*msg)); | 992 memcpy(mem->pathname + sizeof(socketcall_req.args), msg, sizeof(*msg)); |
| 992 SecureMem::sendSystemCall(threadFdPub, true, parentProc, mem, | 993 SecureMem::sendSystemCall(threadFdPub, true, parentMapsFd, mem, |
| 993 __NR_socketcall, socketcall_req.call, | 994 __NR_socketcall, socketcall_req.call, |
| 994 mem->pathname - (char*)mem + (char*)mem->self); | 995 mem->pathname - (char*)mem + (char*)mem->self); |
| 995 return true; | 996 return true; |
| 996 } | 997 } |
| 997 case SYS_RECVMSG: | 998 case SYS_RECVMSG: |
| 998 // Receiving messages is general not security critical. | 999 // Receiving messages is general not security critical. |
| 999 if (socketcall_req.args.recvmsg.flags & | 1000 if (socketcall_req.args.recvmsg.flags & |
| 1000 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { | 1001 ~(MSG_DONTWAIT|MSG_OOB|MSG_PEEK|MSG_TRUNC|MSG_WAITALL)) { |
| 1001 goto deny; | 1002 goto deny; |
| 1002 } | 1003 } |
| 1003 goto accept_complex; | 1004 goto accept_complex; |
| 1004 default: | 1005 default: |
| 1005 deny: | 1006 deny: |
| 1006 SecureMem::abandonSystemCall(threadFd, rc); | 1007 SecureMem::abandonSystemCall(threadFd, rc); |
| 1007 return false; | 1008 return false; |
| 1008 } | 1009 } |
| 1009 } | 1010 } |
| 1010 | 1011 |
| 1011 #endif | 1012 #endif |
| 1012 | 1013 |
| 1013 } // namespace | 1014 } // namespace |
| OLD | NEW |