1*b1cdbd2cSJim Jagielski /************************************************************** 2*b1cdbd2cSJim Jagielski * 3*b1cdbd2cSJim Jagielski * Licensed to the Apache Software Foundation (ASF) under one 4*b1cdbd2cSJim Jagielski * or more contributor license agreements. See the NOTICE file 5*b1cdbd2cSJim Jagielski * distributed with this work for additional information 6*b1cdbd2cSJim Jagielski * regarding copyright ownership. The ASF licenses this file 7*b1cdbd2cSJim Jagielski * to you under the Apache License, Version 2.0 (the 8*b1cdbd2cSJim Jagielski * "License"); you may not use this file except in compliance 9*b1cdbd2cSJim Jagielski * with the License. You may obtain a copy of the License at 10*b1cdbd2cSJim Jagielski * 11*b1cdbd2cSJim Jagielski * http://www.apache.org/licenses/LICENSE-2.0 12*b1cdbd2cSJim Jagielski * 13*b1cdbd2cSJim Jagielski * Unless required by applicable law or agreed to in writing, 14*b1cdbd2cSJim Jagielski * software distributed under the License is distributed on an 15*b1cdbd2cSJim Jagielski * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*b1cdbd2cSJim Jagielski * KIND, either express or implied. See the License for the 17*b1cdbd2cSJim Jagielski * specific language governing permissions and limitations 18*b1cdbd2cSJim Jagielski * under the License. 19*b1cdbd2cSJim Jagielski * 20*b1cdbd2cSJim Jagielski *************************************************************/ 21*b1cdbd2cSJim Jagielski 22*b1cdbd2cSJim Jagielski 23*b1cdbd2cSJim Jagielski 24*b1cdbd2cSJim Jagielski // MARKER(update_precomp.py): autogen include statement, do not remove 25*b1cdbd2cSJim Jagielski #include "precompiled_sal.hxx" 26*b1cdbd2cSJim Jagielski 27*b1cdbd2cSJim Jagielski /** test coder preface: 28*b1cdbd2cSJim Jagielski 1. the BSD socket function will meet "unresolved external symbol error" on Windows platform 29*b1cdbd2cSJim Jagielski if you are not including ws2_32.lib in makefile.mk, the including format will be like this: 30*b1cdbd2cSJim Jagielski 31*b1cdbd2cSJim Jagielski .IF "$(GUI)" == "WNT" 32*b1cdbd2cSJim Jagielski SHL1STDLIBS += $(SOLARLIBDIR)$/cppunit.lib 33*b1cdbd2cSJim Jagielski SHL1STDLIBS += ws2_32.lib 34*b1cdbd2cSJim Jagielski .ENDIF 35*b1cdbd2cSJim Jagielski 36*b1cdbd2cSJim Jagielski likewise on Solaris platform. 37*b1cdbd2cSJim Jagielski .IF "$(GUI)" == "UNX" 38*b1cdbd2cSJim Jagielski SHL1STDLIBS+=$(SOLARLIBDIR)$/libcppunit$(DLLPOSTFIX).a 39*b1cdbd2cSJim Jagielski SHL1STDLIBS += -lsocket -ldl -lnsl 40*b1cdbd2cSJim Jagielski .ENDIF 41*b1cdbd2cSJim Jagielski 42*b1cdbd2cSJim Jagielski 2. since the Socket implementation of osl is only IPv4 oriented, our test are mainly focus on IPv4 43*b1cdbd2cSJim Jagielski category. 44*b1cdbd2cSJim Jagielski 45*b1cdbd2cSJim Jagielski 3. some fragment of Socket source implementation are lack of comment so it is hard for testers 46*b1cdbd2cSJim Jagielski guess what the exact functionality or usage of a member. Hope the Socket section's comment 47*b1cdbd2cSJim Jagielski will be added. 48*b1cdbd2cSJim Jagielski 49*b1cdbd2cSJim Jagielski 4. following functions are declared but not implemented: 50*b1cdbd2cSJim Jagielski inline sal_Bool SAL_CALL operator== (const SocketAddr & Addr) const; 51*b1cdbd2cSJim Jagielski */ 52*b1cdbd2cSJim Jagielski 53*b1cdbd2cSJim Jagielski //------------------------------------------------------------------------ 54*b1cdbd2cSJim Jagielski // include files 55*b1cdbd2cSJim Jagielski //------------------------------------------------------------------------ 56*b1cdbd2cSJim Jagielski 57*b1cdbd2cSJim Jagielski #include <testshl/simpleheader.hxx> 58*b1cdbd2cSJim Jagielski 59*b1cdbd2cSJim Jagielski #include "osl_Socket_Const.h" 60*b1cdbd2cSJim Jagielski #include "sockethelper.hxx" 61*b1cdbd2cSJim Jagielski 62*b1cdbd2cSJim Jagielski using namespace osl; 63*b1cdbd2cSJim Jagielski using namespace rtl; 64*b1cdbd2cSJim Jagielski 65*b1cdbd2cSJim Jagielski #define IP_PORT_FTP 21 66*b1cdbd2cSJim Jagielski #define IP_PORT_MYPORT9 8897 67*b1cdbd2cSJim Jagielski #define IP_PORT_MYPORT4 8885 68*b1cdbd2cSJim Jagielski #define IP_PORT_MYPORT3 8884 69*b1cdbd2cSJim Jagielski 70*b1cdbd2cSJim Jagielski //------------------------------------------------------------------------ 71*b1cdbd2cSJim Jagielski // helper functions 72*b1cdbd2cSJim Jagielski //------------------------------------------------------------------------ 73*b1cdbd2cSJim Jagielski 74*b1cdbd2cSJim Jagielski // just used to test socket::close() when accepting 75*b1cdbd2cSJim Jagielski class AcceptorThread : public Thread 76*b1cdbd2cSJim Jagielski { 77*b1cdbd2cSJim Jagielski ::osl::AcceptorSocket asAcceptorSocket; 78*b1cdbd2cSJim Jagielski ::rtl::OUString aHostIP; 79*b1cdbd2cSJim Jagielski sal_Bool bOK; 80*b1cdbd2cSJim Jagielski protected: run()81*b1cdbd2cSJim Jagielski void SAL_CALL run( ) 82*b1cdbd2cSJim Jagielski { 83*b1cdbd2cSJim Jagielski ::osl::SocketAddr saLocalSocketAddr( aHostIP, IP_PORT_MYPORT9 ); 84*b1cdbd2cSJim Jagielski ::osl::StreamSocket ssStreamConnection; 85*b1cdbd2cSJim Jagielski 86*b1cdbd2cSJim Jagielski asAcceptorSocket.setOption( osl_Socket_OptionReuseAddr, 1 ); //integer not sal_Bool : sal_True); 87*b1cdbd2cSJim Jagielski sal_Bool bOK1 = asAcceptorSocket.bind( saLocalSocketAddr ); 88*b1cdbd2cSJim Jagielski if ( sal_True != bOK1 ) 89*b1cdbd2cSJim Jagielski { 90*b1cdbd2cSJim Jagielski t_print("# AcceptorSocket bind address failed.\n" ) ; 91*b1cdbd2cSJim Jagielski return; 92*b1cdbd2cSJim Jagielski } 93*b1cdbd2cSJim Jagielski sal_Bool bOK2 = asAcceptorSocket.listen( 1 ); 94*b1cdbd2cSJim Jagielski if ( sal_True != bOK2 ) 95*b1cdbd2cSJim Jagielski { 96*b1cdbd2cSJim Jagielski t_print("# AcceptorSocket listen address failed.\n" ) ; 97*b1cdbd2cSJim Jagielski return; 98*b1cdbd2cSJim Jagielski } 99*b1cdbd2cSJim Jagielski 100*b1cdbd2cSJim Jagielski asAcceptorSocket.enableNonBlockingMode( sal_False ); 101*b1cdbd2cSJim Jagielski 102*b1cdbd2cSJim Jagielski oslSocketResult eResult = asAcceptorSocket.acceptConnection( ssStreamConnection ); 103*b1cdbd2cSJim Jagielski if (eResult != osl_Socket_Ok ) 104*b1cdbd2cSJim Jagielski { 105*b1cdbd2cSJim Jagielski bOK = sal_True; 106*b1cdbd2cSJim Jagielski t_print("AcceptorThread: acceptConnection failed! \n"); 107*b1cdbd2cSJim Jagielski } 108*b1cdbd2cSJim Jagielski } 109*b1cdbd2cSJim Jagielski public: AcceptorThread(::osl::AcceptorSocket & asSocket,::rtl::OUString const & aBindIP)110*b1cdbd2cSJim Jagielski AcceptorThread(::osl::AcceptorSocket & asSocket, ::rtl::OUString const& aBindIP ) 111*b1cdbd2cSJim Jagielski : asAcceptorSocket( asSocket ), aHostIP( aBindIP ) 112*b1cdbd2cSJim Jagielski { 113*b1cdbd2cSJim Jagielski bOK = sal_False; 114*b1cdbd2cSJim Jagielski } 115*b1cdbd2cSJim Jagielski isOK()116*b1cdbd2cSJim Jagielski sal_Bool isOK() { return bOK; } 117*b1cdbd2cSJim Jagielski ~AcceptorThread()118*b1cdbd2cSJim Jagielski ~AcceptorThread( ) 119*b1cdbd2cSJim Jagielski { 120*b1cdbd2cSJim Jagielski if ( isRunning( ) ) 121*b1cdbd2cSJim Jagielski { 122*b1cdbd2cSJim Jagielski asAcceptorSocket.shutdown(); 123*b1cdbd2cSJim Jagielski t_print("# error: Acceptor thread not terminated.\n" ); 124*b1cdbd2cSJim Jagielski } 125*b1cdbd2cSJim Jagielski } 126*b1cdbd2cSJim Jagielski }; 127*b1cdbd2cSJim Jagielski 128*b1cdbd2cSJim Jagielski namespace osl_AcceptorSocket 129*b1cdbd2cSJim Jagielski { 130*b1cdbd2cSJim Jagielski 131*b1cdbd2cSJim Jagielski /** testing the methods: 132*b1cdbd2cSJim Jagielski inline AcceptorSocket(oslAddrFamily Family = osl_Socket_FamilyInet, 133*b1cdbd2cSJim Jagielski oslProtocol Protocol = osl_Socket_ProtocolIp, 134*b1cdbd2cSJim Jagielski oslSocketType Type = osl_Socket_TypeStream); 135*b1cdbd2cSJim Jagielski */ 136*b1cdbd2cSJim Jagielski 137*b1cdbd2cSJim Jagielski class ctors : public CppUnit::TestFixture 138*b1cdbd2cSJim Jagielski { 139*b1cdbd2cSJim Jagielski public: 140*b1cdbd2cSJim Jagielski ctors_001()141*b1cdbd2cSJim Jagielski void ctors_001() 142*b1cdbd2cSJim Jagielski { 143*b1cdbd2cSJim Jagielski /// Socket constructor. 144*b1cdbd2cSJim Jagielski ::osl::AcceptorSocket asSocket( osl_Socket_FamilyInet, osl_Socket_ProtocolIp, osl_Socket_TypeStream ); 145*b1cdbd2cSJim Jagielski 146*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "test for ctors_001 constructor function: check if the acceptor socket was created successfully.", 147*b1cdbd2cSJim Jagielski osl_Socket_TypeStream == asSocket.getType( ) ); 148*b1cdbd2cSJim Jagielski } 149*b1cdbd2cSJim Jagielski 150*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE( ctors ); 151*b1cdbd2cSJim Jagielski CPPUNIT_TEST( ctors_001 ); 152*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE_END(); 153*b1cdbd2cSJim Jagielski 154*b1cdbd2cSJim Jagielski }; // class ctors 155*b1cdbd2cSJim Jagielski 156*b1cdbd2cSJim Jagielski #if 0 /* OBSOLETE */ 157*b1cdbd2cSJim Jagielski class operator_assign : public CppUnit::TestFixture 158*b1cdbd2cSJim Jagielski { 159*b1cdbd2cSJim Jagielski public: 160*b1cdbd2cSJim Jagielski 161*b1cdbd2cSJim Jagielski void assign_001() 162*b1cdbd2cSJim Jagielski { 163*b1cdbd2cSJim Jagielski #if defined(LINUX) 164*b1cdbd2cSJim Jagielski ::osl::AcceptorSocket asSocket( osl_Socket_FamilyInet, osl_Socket_ProtocolIp, osl_Socket_TypeStream ); 165*b1cdbd2cSJim Jagielski ::osl::AcceptorSocket asSocketAssign( osl_Socket_FamilyInet, osl_Socket_ProtocolIp, osl_Socket_TypeStream ); 166*b1cdbd2cSJim Jagielski asSocket.setOption( osl_Socket_OptionReuseAddr, 1); 167*b1cdbd2cSJim Jagielski ::osl::SocketAddr saSocketAddr( rtl::OUString::createFromAscii("127.0.0.1"), IP_PORT_MYPORT4 ); 168*b1cdbd2cSJim Jagielski asSocket.bind( saSocketAddr ); 169*b1cdbd2cSJim Jagielski 170*b1cdbd2cSJim Jagielski AcceptorThread myAcceptorThread( asSocketAssign, rtl::OUString::createFromAscii("127.0.0.1") ); 171*b1cdbd2cSJim Jagielski myAcceptorThread.create(); 172*b1cdbd2cSJim Jagielski 173*b1cdbd2cSJim Jagielski thread_sleep( 1 ); 174*b1cdbd2cSJim Jagielski //when accepting, assign another socket to the socket, the thread will not be closed, so is blocking 175*b1cdbd2cSJim Jagielski asSocketAssign = asSocket; 176*b1cdbd2cSJim Jagielski 177*b1cdbd2cSJim Jagielski t_print("#asSocketAssign port number is %d\n", asSocketAssign.getLocalPort() ); 178*b1cdbd2cSJim Jagielski 179*b1cdbd2cSJim Jagielski asSocketAssign.shutdown(); 180*b1cdbd2cSJim Jagielski myAcceptorThread.join(); 181*b1cdbd2cSJim Jagielski 182*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "test for close when is accepting: the socket will quit accepting status.", 183*b1cdbd2cSJim Jagielski myAcceptorThread.isOK() == sal_True ); 184*b1cdbd2cSJim Jagielski 185*b1cdbd2cSJim Jagielski 186*b1cdbd2cSJim Jagielski #endif /* LINUX */ 187*b1cdbd2cSJim Jagielski } 188*b1cdbd2cSJim Jagielski 189*b1cdbd2cSJim Jagielski 190*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE( operator_assign ); 191*b1cdbd2cSJim Jagielski CPPUNIT_TEST( assign_001 ); 192*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE_END(); 193*b1cdbd2cSJim Jagielski 194*b1cdbd2cSJim Jagielski }; // class operator_assign 195*b1cdbd2cSJim Jagielski #endif /* OBSOLETE */ 196*b1cdbd2cSJim Jagielski 197*b1cdbd2cSJim Jagielski /** testing the method: 198*b1cdbd2cSJim Jagielski inline sal_Bool SAL_CALL listen(sal_Int32 MaxPendingConnections= -1); 199*b1cdbd2cSJim Jagielski inline oslSocketResult SAL_CALL acceptConnection( StreamSocket& Connection); 200*b1cdbd2cSJim Jagielski inline oslSocketResult SAL_CALL acceptConnection( StreamSocket& Connection, SocketAddr & PeerAddr); 201*b1cdbd2cSJim Jagielski */ 202*b1cdbd2cSJim Jagielski 203*b1cdbd2cSJim Jagielski class listen_accept : public CppUnit::TestFixture 204*b1cdbd2cSJim Jagielski { 205*b1cdbd2cSJim Jagielski public: 206*b1cdbd2cSJim Jagielski TimeValue *pTimeout; 207*b1cdbd2cSJim Jagielski ::osl::AcceptorSocket asAcceptorSocket; 208*b1cdbd2cSJim Jagielski ::osl::ConnectorSocket csConnectorSocket; 209*b1cdbd2cSJim Jagielski 210*b1cdbd2cSJim Jagielski 211*b1cdbd2cSJim Jagielski // initialization setUp()212*b1cdbd2cSJim Jagielski void setUp( ) 213*b1cdbd2cSJim Jagielski { 214*b1cdbd2cSJim Jagielski pTimeout = ( TimeValue* )malloc( sizeof( TimeValue ) ); 215*b1cdbd2cSJim Jagielski pTimeout->Seconds = 3; 216*b1cdbd2cSJim Jagielski pTimeout->Nanosec = 0; 217*b1cdbd2cSJim Jagielski asAcceptorSocket.setOption( osl_Socket_OptionReuseAddr, 1); 218*b1cdbd2cSJim Jagielski // sHandle = osl_createSocket( osl_Socket_FamilyInet, osl_Socket_TypeStream, osl_Socket_ProtocolIp ); 219*b1cdbd2cSJim Jagielski } 220*b1cdbd2cSJim Jagielski tearDown()221*b1cdbd2cSJim Jagielski void tearDown( ) 222*b1cdbd2cSJim Jagielski { 223*b1cdbd2cSJim Jagielski free( pTimeout ); 224*b1cdbd2cSJim Jagielski // sHandle = NULL; 225*b1cdbd2cSJim Jagielski asAcceptorSocket.close( ); 226*b1cdbd2cSJim Jagielski csConnectorSocket.close( ); 227*b1cdbd2cSJim Jagielski } 228*b1cdbd2cSJim Jagielski 229*b1cdbd2cSJim Jagielski listen_accept_001()230*b1cdbd2cSJim Jagielski void listen_accept_001() 231*b1cdbd2cSJim Jagielski { 232*b1cdbd2cSJim Jagielski ::osl::SocketAddr saLocalSocketAddr( rtl::OUString::createFromAscii("127.0.0.1"), IP_PORT_MYPORT3 ); 233*b1cdbd2cSJim Jagielski ::osl::SocketAddr saTargetSocketAddr( rtl::OUString::createFromAscii("127.0.0.1"), IP_PORT_MYPORT3 ); 234*b1cdbd2cSJim Jagielski ::osl::StreamSocket ssConnection; 235*b1cdbd2cSJim Jagielski 236*b1cdbd2cSJim Jagielski /// launch server socket 237*b1cdbd2cSJim Jagielski sal_Bool bOK1 = asAcceptorSocket.bind( saLocalSocketAddr ); 238*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "AcceptorSocket bind address failed.", sal_True == bOK1 ); 239*b1cdbd2cSJim Jagielski sal_Bool bOK2 = asAcceptorSocket.listen( 1 ); 240*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "AcceptorSocket listen failed.", sal_True == bOK2 ); 241*b1cdbd2cSJim Jagielski asAcceptorSocket.enableNonBlockingMode( sal_True ); 242*b1cdbd2cSJim Jagielski 243*b1cdbd2cSJim Jagielski /// launch client socket 244*b1cdbd2cSJim Jagielski csConnectorSocket.connect( saTargetSocketAddr, pTimeout ); /// connecting to server... 245*b1cdbd2cSJim Jagielski 246*b1cdbd2cSJim Jagielski oslSocketResult eResult = asAcceptorSocket.acceptConnection(ssConnection); /// waiting for incoming connection... 247*b1cdbd2cSJim Jagielski 248*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "test for listen_accept function: try to create a connection with remote host, using listen and accept.", 249*b1cdbd2cSJim Jagielski ( osl_Socket_Ok == eResult ) ); 250*b1cdbd2cSJim Jagielski } 251*b1cdbd2cSJim Jagielski listen_accept_002()252*b1cdbd2cSJim Jagielski void listen_accept_002() 253*b1cdbd2cSJim Jagielski { 254*b1cdbd2cSJim Jagielski ::osl::SocketAddr saLocalSocketAddr( rtl::OUString::createFromAscii("127.0.0.1"), IP_PORT_MYPORT4 ); 255*b1cdbd2cSJim Jagielski ::osl::SocketAddr saTargetSocketAddr( rtl::OUString::createFromAscii("127.0.0.1"), IP_PORT_MYPORT4 ); 256*b1cdbd2cSJim Jagielski ::osl::SocketAddr saPeerSocketAddr( rtl::OUString::createFromAscii("129.158.217.202"), IP_PORT_FTP ); 257*b1cdbd2cSJim Jagielski ::osl::StreamSocket ssConnection; 258*b1cdbd2cSJim Jagielski 259*b1cdbd2cSJim Jagielski /// launch server socket 260*b1cdbd2cSJim Jagielski sal_Bool bOK1 = asAcceptorSocket.bind( saLocalSocketAddr ); 261*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "AcceptorSocket bind address failed.", sal_True == bOK1 ); 262*b1cdbd2cSJim Jagielski sal_Bool bOK2 = asAcceptorSocket.listen( 1 ); 263*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "AcceptorSocket listen failed.", sal_True == bOK2 ); 264*b1cdbd2cSJim Jagielski asAcceptorSocket.enableNonBlockingMode( sal_True ); 265*b1cdbd2cSJim Jagielski 266*b1cdbd2cSJim Jagielski /// launch client socket 267*b1cdbd2cSJim Jagielski csConnectorSocket.connect( saTargetSocketAddr, pTimeout ); /// connecting to server... 268*b1cdbd2cSJim Jagielski 269*b1cdbd2cSJim Jagielski oslSocketResult eResult = asAcceptorSocket.acceptConnection(ssConnection, saPeerSocketAddr); /// waiting for incoming connection... 270*b1cdbd2cSJim Jagielski 271*b1cdbd2cSJim Jagielski CPPUNIT_ASSERT_MESSAGE( "test for listen_accept function: try to create a connection with remote host, using listen and accept, accept with peer address.", 272*b1cdbd2cSJim Jagielski ( sal_True == bOK2 ) && 273*b1cdbd2cSJim Jagielski ( osl_Socket_Ok == eResult ) && 274*b1cdbd2cSJim Jagielski ( sal_True == compareSocketAddr( saPeerSocketAddr, saLocalSocketAddr ) ) ); 275*b1cdbd2cSJim Jagielski } 276*b1cdbd2cSJim Jagielski 277*b1cdbd2cSJim Jagielski 278*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE( listen_accept ); 279*b1cdbd2cSJim Jagielski CPPUNIT_TEST( listen_accept_001 ); 280*b1cdbd2cSJim Jagielski CPPUNIT_TEST( listen_accept_002 ); 281*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE_END(); 282*b1cdbd2cSJim Jagielski 283*b1cdbd2cSJim Jagielski }; // class listen_accept 284*b1cdbd2cSJim Jagielski 285*b1cdbd2cSJim Jagielski 286*b1cdbd2cSJim Jagielski // ----------------------------------------------------------------------------- 287*b1cdbd2cSJim Jagielski 288*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_AcceptorSocket::ctors, "osl_AcceptorSocket"); 289*b1cdbd2cSJim Jagielski //CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_AcceptorSocket::operator_assign, "osl_AcceptorSocket"); 290*b1cdbd2cSJim Jagielski CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_AcceptorSocket::listen_accept, "osl_AcceptorSocket"); 291*b1cdbd2cSJim Jagielski 292*b1cdbd2cSJim Jagielski } // namespace osl_AcceptorSocket 293*b1cdbd2cSJim Jagielski 294*b1cdbd2cSJim Jagielski // ----------------------------------------------------------------------------- 295*b1cdbd2cSJim Jagielski 296*b1cdbd2cSJim Jagielski // this macro creates an empty function, which will called by the RegisterAllFunctions() 297*b1cdbd2cSJim Jagielski // to let the user the possibility to also register some functions by hand. 298*b1cdbd2cSJim Jagielski NOADDITIONAL; 299