001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.hadoop.util; 020 021 import java.io.DataInput; 022 import java.io.IOException; 023 024 import org.apache.hadoop.ipc.RPC; 025 import org.apache.hadoop.ipc.protobuf.IpcConnectionContextProtos.IpcConnectionContextProto; 026 import org.apache.hadoop.ipc.protobuf.IpcConnectionContextProtos.UserInformationProto; 027 import org.apache.hadoop.ipc.protobuf.RpcHeaderProtos.*; 028 import org.apache.hadoop.security.SaslRpcServer.AuthMethod; 029 import org.apache.hadoop.security.UserGroupInformation; 030 import org.htrace.Span; 031 import org.htrace.Trace; 032 033 import com.google.protobuf.ByteString; 034 035 public abstract class ProtoUtil { 036 037 /** 038 * Read a variable length integer in the same format that ProtoBufs encodes. 039 * @param in the input stream to read from 040 * @return the integer 041 * @throws IOException if it is malformed or EOF. 042 */ 043 public static int readRawVarint32(DataInput in) throws IOException { 044 byte tmp = in.readByte(); 045 if (tmp >= 0) { 046 return tmp; 047 } 048 int result = tmp & 0x7f; 049 if ((tmp = in.readByte()) >= 0) { 050 result |= tmp << 7; 051 } else { 052 result |= (tmp & 0x7f) << 7; 053 if ((tmp = in.readByte()) >= 0) { 054 result |= tmp << 14; 055 } else { 056 result |= (tmp & 0x7f) << 14; 057 if ((tmp = in.readByte()) >= 0) { 058 result |= tmp << 21; 059 } else { 060 result |= (tmp & 0x7f) << 21; 061 result |= (tmp = in.readByte()) << 28; 062 if (tmp < 0) { 063 // Discard upper 32 bits. 064 for (int i = 0; i < 5; i++) { 065 if (in.readByte() >= 0) { 066 return result; 067 } 068 } 069 throw new IOException("Malformed varint"); 070 } 071 } 072 } 073 } 074 return result; 075 } 076 077 078 /** 079 * This method creates the connection context using exactly the same logic 080 * as the old connection context as was done for writable where 081 * the effective and real users are set based on the auth method. 082 * 083 */ 084 public static IpcConnectionContextProto makeIpcConnectionContext( 085 final String protocol, 086 final UserGroupInformation ugi, final AuthMethod authMethod) { 087 IpcConnectionContextProto.Builder result = IpcConnectionContextProto.newBuilder(); 088 if (protocol != null) { 089 result.setProtocol(protocol); 090 } 091 UserInformationProto.Builder ugiProto = UserInformationProto.newBuilder(); 092 if (ugi != null) { 093 /* 094 * In the connection context we send only additional user info that 095 * is not derived from the authentication done during connection setup. 096 */ 097 if (authMethod == AuthMethod.KERBEROS) { 098 // Real user was established as part of the connection. 099 // Send effective user only. 100 ugiProto.setEffectiveUser(ugi.getUserName()); 101 } else if (authMethod == AuthMethod.TOKEN) { 102 // With token, the connection itself establishes 103 // both real and effective user. Hence send none in header. 104 } else { // Simple authentication 105 // No user info is established as part of the connection. 106 // Send both effective user and real user 107 ugiProto.setEffectiveUser(ugi.getUserName()); 108 if (ugi.getRealUser() != null) { 109 ugiProto.setRealUser(ugi.getRealUser().getUserName()); 110 } 111 } 112 } 113 result.setUserInfo(ugiProto); 114 return result.build(); 115 } 116 117 public static UserGroupInformation getUgi(IpcConnectionContextProto context) { 118 if (context.hasUserInfo()) { 119 UserInformationProto userInfo = context.getUserInfo(); 120 return getUgi(userInfo); 121 } else { 122 return null; 123 } 124 } 125 126 public static UserGroupInformation getUgi(UserInformationProto userInfo) { 127 UserGroupInformation ugi = null; 128 String effectiveUser = userInfo.hasEffectiveUser() ? userInfo 129 .getEffectiveUser() : null; 130 String realUser = userInfo.hasRealUser() ? userInfo.getRealUser() : null; 131 if (effectiveUser != null) { 132 if (realUser != null) { 133 UserGroupInformation realUserUgi = UserGroupInformation 134 .createRemoteUser(realUser); 135 ugi = UserGroupInformation 136 .createProxyUser(effectiveUser, realUserUgi); 137 } else { 138 ugi = org.apache.hadoop.security.UserGroupInformation 139 .createRemoteUser(effectiveUser); 140 } 141 } 142 return ugi; 143 } 144 145 static RpcKindProto convert(RPC.RpcKind kind) { 146 switch (kind) { 147 case RPC_BUILTIN: return RpcKindProto.RPC_BUILTIN; 148 case RPC_WRITABLE: return RpcKindProto.RPC_WRITABLE; 149 case RPC_PROTOCOL_BUFFER: return RpcKindProto.RPC_PROTOCOL_BUFFER; 150 } 151 return null; 152 } 153 154 155 public static RPC.RpcKind convert( RpcKindProto kind) { 156 switch (kind) { 157 case RPC_BUILTIN: return RPC.RpcKind.RPC_BUILTIN; 158 case RPC_WRITABLE: return RPC.RpcKind.RPC_WRITABLE; 159 case RPC_PROTOCOL_BUFFER: return RPC.RpcKind.RPC_PROTOCOL_BUFFER; 160 } 161 return null; 162 } 163 164 public static RpcRequestHeaderProto makeRpcRequestHeader(RPC.RpcKind rpcKind, 165 RpcRequestHeaderProto.OperationProto operation, int callId, 166 int retryCount, byte[] uuid) { 167 RpcRequestHeaderProto.Builder result = RpcRequestHeaderProto.newBuilder(); 168 result.setRpcKind(convert(rpcKind)).setRpcOp(operation).setCallId(callId) 169 .setRetryCount(retryCount).setClientId(ByteString.copyFrom(uuid)); 170 171 // Add tracing info if we are currently tracing. 172 if (Trace.isTracing()) { 173 Span s = Trace.currentSpan(); 174 result.setTraceInfo(RPCTraceInfoProto.newBuilder() 175 .setParentId(s.getSpanId()) 176 .setTraceId(s.getTraceId()).build()); 177 } 178 179 return result.build(); 180 } 181 }